Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tipos de dato

TipoSintaxisDóndeDescripción
charchar(longitud)@Fields, @VariablesCadena de longitud fija. longitud requerida (1-32.766 en @Fields; 1-16.773.104 en @Variables).
varcharvarchar(longitud)sólo @VariablesCadena de longitud variable. longitud requerida (1-16.773.100). Length(...) devuelve el contenido real en tiempo de ejecución, a diferencia de char.
numbernumber(precisión, escala)@Fields, @VariablesNumérico. precisión requerida (1-30 en @Fields; 1-63 en @Variables); escala opcional, default 0.
datedate@Fields, @VariablesFecha, sin parámetros. En @Fields no puede llevar Default(...).
timetime@Fields, @VariablesHora (hora/minuto/segundo), sin parámetros. En @Fields no puede llevar Default(...).
timestamptimestamp@Fields, @VariablesFecha y hora combinadas (con microsegundos), sin parámetros. En @Fields no puede llevar Default(...).
fieldfield(NombreCampo) o field(Tabla.Campo)sólo @VariablesHereda el tipo de un campo de @Structure de alguna tabla referenciada. Ver field: heredar el tipo de un campo.
listlist(tipo)sólo @VariablesLista dinámica de tipo (escalar, struct(Template) o field(...); no admite otro list ni matrix anidado, ver E00283). Sin tamaño en la declaración — la capacidad interna máxima es fija (9999 elementos) y no configurable. Se lee/escribe un elemento con &Variable[índice]; se agrega/quita/consulta con métodos (ver List: métodos).
matrixmatrix(filas, cols, tipo)sólo @VariablesGrilla 2D de tamaño fijo filas x cols de tipo. Se lee/escribe un elemento con &Variable[fila, col].
structstruct(NombreTemplate)sólo @VariablesInstancia de un template struct NombreTemplate [ ... ] definido inline en el ámbito global (ver Struct: templates y acceso a miembro). Se accede a un miembro con &Variable.Miembro.

Las constantes (const Nombre tipo = valor) sólo admiten char, varchar y number; ver @Variables: Constantes.

Ejemplos

@Variables {
    global {
        Usuario      char(10)
        Destino      varchar(200)
        Saldo        number(9,2)
        FechaAlta    date
        HoraAlta     time
        FechaHoraAlta timestamp
        Estado       field(ADSTS50)
        Lista1       list(number(5))
        Tablero      matrix(8, 8, char(1))
    }
}

field: heredar el tipo de un campo

field(...) toma el tipo del campo indicado, buscándolo en las tablas declaradas en @References/Tables. Tiene dos formas:

  • field(Campo): busca el campo en todas las tablas referenciadas. Si existe en más de una, se reporta E00149 (ambiguo): “El campo ‘{field}’ de la variable field(…) es ambiguo: existe en más de una tabla de @References/Tables; indique la tabla con field(Tabla.Campo).”
  • field(Tabla.Campo): busca el campo sólo en esa tabla, para desambiguar. No distingue mayúsculas en la tabla ni en el campo. Si la tabla no está referenciada o el campo no existe en ella, se reporta E00148 (campo no encontrado).

El tipo resultante es el del campo en ambas formas. Ambas valen en cualquier ámbito de @Variables y también como miembro de un struct.

// Las tablas Clientes y Proveedores (en @References/Tables) tienen un campo Codigo
@Variables {
    global {
        struct Contacto [
            Codigo  field(Clientes.Codigo)
        ]
        CodCliente    field(Clientes.Codigo)
        CodProveedor  field(Proveedores.Codigo)
        Nombre        field(Clientes.Nombre)   // también válido si el campo no es ambiguo
    }
}

Con field(Codigo) a secas, este ejemplo reportaría E00149.

Interoperabilidad char/varchar

char y varchar interoperan en asignaciones — la regla es sólo de longitud (el origen debe entrar en el destino), sin importar cuál de los dos es fijo o variable:

&VarcharDestino = &CharOrigen  // válido si longitud(CharOrigen) <= longitud(VarcharDestino)
&CharDestino    = &VarcharOrigen

List/Matrix: indexación

&Lista1[1] = 100
&Tablero[3, 5] = "X"

El índice es 1-based. List/Matrix sólo son válidos en @Variables, nunca en @Fields.

List: métodos

list no lleva tamaño en la declaración (list(tipo), sin parámetro de capacidad): la capacidad interna máxima es fija en 9999 elementos, definida por el compilador y no configurable desde el lenguaje. En vez de fijar el tamaño de antemano, se administra el contenido con métodos, con sintaxis de miembro (&Variable.Metodo(...)):

MétodoUsoDescripción
Add(valor)sentenciaAgrega valor al final de la lista. valor debe ser compatible en tipo con el elemento de la lista (si el elemento es struct(Template), valor debe ser una &Variable de ese mismo template exacto).
Clear()sentenciaVacía la lista: limpia los elementos en uso (blancos o ceros) y deja la cuenta en 0.
Init()sentenciaDeja todos los elementos de la lista (los 9999) en su valor vacío (blancos en char/varchar, ceros en number, valor inicial en date/time/timestamp, struct limpios) y la cuenta (Count()) en 0. No recibe argumentos (E00282) ni se usa como expresión (E00280).
Remove(indice)sentenciaQuita el elemento en la posición indice (1-based, tipo number), recorre los siguientes una posición hacia atrás y limpia el último espacio que queda libre. Un indice fuera de 1..Count() no hace nada.
Split(texto[, separadores[, AllSep]])sentenciaReemplaza el contenido de la lista (limpia lo anterior) con los trozos de texto. Ver Split().
Count()expresiónDevuelve la cantidad actual de elementos (tipo number). Sólo se puede usar dentro de una expresión, nunca como sentencia suelta.
Json()expresiónDevuelve la lista como arreglo JSON (sólo list(struct(...))). Ver Json().
&Lista1.Add(10)
&Lista1.Add(20)
&Lista1.Add(30)

For &i = 1 To &Lista1.Count()
    // ... &Lista1[&i] ...
EndFor

&Lista1.Remove(2)
&Lista1.Clear()
&Lista1.Init()

No existe un For Each sobre una lista en memoria — el recorrido se hace con el For clásico por contador (For &i = 1 To &Lista.Count()) más el indexado &Variable[&i] (For Each In sigue siendo exclusivo de tablas de base de datos, ver @Source: sentencias de control).

Borrado real. Clear(), Remove(indice) y JsonSplit (al reemplazar el contenido anterior) limpian de verdad las posiciones que dejan de usarse, no sólo ajustan el contador. Así se mantiene siempre esta invariante: todo elemento posterior al último está vacío (blancos o ceros). Un programa RPG que reciba el arreglo (ver @Parameters) puede leerlo hasta la primera línea vacía. Con 9999 elementos no queda ninguna posición vacía después del último.

Init() frente a Clear(). En RPG, Init() es un único clear del arreglo completo (más contador = 0), sin bucle. Clear() sólo limpia hasta el contador: es más rápido cuando se usa con pocas filas, pero deja intactos los elementos escritos por índice (&Lista[10] = x) sin pasar por Add(), porque esas escrituras no mueven el contador. Init() limpia toda la capacidad (con char(250) son unos 2,5 MB), por lo que conviene usarlo al inicio o cuando haga falta, no dentro de un bucle muy repetido.

El caso típico de Init() es usar una list como arreglo de tamaño fijo (&Trama[1], &Trama[10]) que luego se pasa a otro programa con Call:

&Trama.Init()
&Trama[1] = "Cabecera"
&Trama[10] = "Pie"
// Clear() no limpiaría &Trama[10]: se escribió por índice y Count() sigue en 0
&Trama.Init()

Ver el ejemplo completo en example/programs/PINIT01.warp.

Nota: Clear(), Remove() y JsonSplit dejan vacío lo que queda después del último elemento.

Si Add(valor) se llama cuando la lista ya está en su capacidad máxima (9999 elementos), el programa emite un dsply visible con el mensaje Lista {nombre} alcanzó su capacidad máxima (9999) y no agrega el elemento — no aborta el programa completo.

list(struct(Template)) es válido (una lista de instancias de un mismo struct). Anidar list/matrix entre sí no lo es, en ninguna combinación: list(list(...)), list(matrix(...)), matrix(list(...)) y matrix(matrix(...)) reportan el error de compilación E00283 (“‘&{variable}’ es un {list/matrix} cuyo elemento no puede ser {list/matrix} (sin equivalente RPG generable): use un tipo escalar, struct(…) o field(…).”), detectado en el análisis semántico — no es posible declarar un arreglo cuyo elemento sea a su vez otro arreglo.

Split(): dividir un texto en una lista

&Lista.Split(texto[, separadores[, AllSep]]) es una sentencia (como Add/Clear; usada como expresión da E00280) y solo vale sobre list(char(n)) o list(varchar(n)) (E00363 si no).

  • Reemplaza el contenido de la lista: limpia lo anterior y deja los trozos de texto, hasta 9999. Al llenarse muestra «Lista X llena (max 9999)» con dsply y descarta el resto. Un trozo más largo que el elemento se recorta.
  • Sin separadores, corta por blancos.
  • Cada carácter de separadores es un separador: "-;" separa por guion y por punto y coma.
  • Sin AllSep, los separadores consecutivos, iniciales y finales se ignoran: "uno,dos,,tres" con "," da 3 trozos.
  • Con AllSep (tercer argumento, la palabra AllSep, que exige indicar separadores), cada separador delimita dos trozos y aparecen los vacíos: el mismo texto da 4 trozos: uno, dos, `` y tres.
  • Un texto vacío da 0 trozos.
  • Los blancos finales de un char de origen se ignoran. Una variable char de separadores se recorta: sus blancos finales no son separador (para un blanco como separador use un literal).
  • texto y separadores deben ser char/varchar (E00359); se admiten de 1 a 3 argumentos (E00358); el tercero debe ser la palabra AllSep (E00361).
&Partes.Split(&Texto, ",")
For &i = 1 To &Partes.Count()
    // ... &Partes[&i] ...
EndFor

&Partes.Split("uno,dos,,tres", ",", AllSep)   // 4 trozos, uno vacío

Requisito de release. Split genera for-each ... in %split(...). En IBM i 7.5 viene de base. En 7.3 y 7.4 hacen falta dos PTF de abril de 2021: el del compilador (5770WDS) y el del runtime (5770SS1), este último en cada sistema donde corra el programa.

ReleasePTF de compiladorPTF de runtime
7.3SI76100SI76098
7.4SI76101 (para TGTRLS V7R3M0: SI76109)SI76099

Sin el PTF de runtime el programa compila, pero falla al ejecutar con MCH4437 (export no encontrado). Es el mismo requisito que Upper y Lower.

AllSep (*ALLSEP) exige además 7.4 TR8 / 7.5 TR2 o sus PTF: 7.4, compilador SI83497 y runtime SI83471; 7.5, compilador SI83480 (para TGTRLS V7R4M0: SI83496) y runtime SI83468. Estos números provienen de un resumen de búsqueda: verifíquelos con IBM.

Json(): convertir un struct o una lista a JSON

&Variable.Json() devuelve el contenido de la variable como texto JSON (varchar). Se puede aplicar a un struct(...) (produce un objeto) o a una list(struct(...)) (produce un arreglo), incluidos los anidados por camino (&env.Json(), &env.Cab.Detalle.Json()). Se usa sólo como expresión, nunca como sentencia suelta (E00294), y no recibe argumentos (E00282):

&jsonObjeto  = &unRegistro.Json()
&jsonArreglo = &listaRegistros.Json()

Para el template Registro [ codigo number(3,0), nombre char(20), monto number(7,2), fecha date ], los resultados tienen esta forma:

{"codigo":1,"nombre":"Ana","monto":1250.75,"fecha":"2026-10-16"}
[{"codigo":1,"nombre":"Ana",...},{"codigo":2,"nombre":"Luis",...}]
  • Claves: el nombre del miembro tal como se declaró en el template, en el orden de declaración.
  • Lista: el arreglo contiene sólo los elementos en uso (Count()).
  • char/varchar: cadena JSON entre comillas, con " y \ escapados y los espacios finales recortados. Los caracteres de control no reciben tratamiento especial.
  • number: número JSON, sin comillas.
  • date: cadena "yyyy-mm-dd".
  • Anidados: un miembro struct(...) genera un objeto y un miembro list(struct(...)) un arreglo, p.ej. {"Cab":{"epm":"a","Detalle":[{"Pgm":"P1","Libreria":"L1"}]},"Total":2}.
  • Los miembros escalares de un struct sólo pueden ser char/varchar/number/date/field; el generador también sabe emitir time ("hh:mm:ss") y timestamp ("yyyy-mm-ddThh:mm:ss.ffffff") por si el tipo de miembro se amplía.

Tamaño de la variable destino. Para un struct, la expresión tiene el tamaño del peor caso: por cada texto, 2 × largo + 2; por cada número, precisión + 2; por cada fecha, 12; más los nombres de clave, comillas, llaves y comas. Con structs anidados el peor caso crece con cada lista anidada (se calcula sobre sus 100 elementos). La variable que recibe el resultado debe poder contenerlo; si no, se reporta el error habitual de asignación incompatible (E00147). Declare el destino con holgura (p.ej. varchar(300) para un template pequeño). Para una list no se exige tamaño de destino, pero si el arreglo generado excede el máximo de un varchar de RPG (16 773 100 caracteres) el programa falla en ejecución, no trunca.

Errores: E00294 (usado como sentencia), E00295 (la variable no es struct ni list), E00296 (otro método sobre un struct; el único válido es Json), E00297 (list de tipos escalares: por ahora no soportado). Ver Diagnósticos.

No requiere librerías ni PTF en IBM i: cuando se usa .Json(), el compilador genera en el RPG los procedimientos de apoyo necesarios (Json_<Template> y JsonList_<Template>). Un ejemplo completo está en example/JSON01.warp.

Para partir el texto resultante en líneas (p.ej. para imprimirlo) ver JsonSplit.

Tramas de ancho fijo

Una trama de ancho fijo es una línea de texto donde cada miembro de un struct ocupa una cantidad fija de posiciones, en el orden de declaración, sin separadores. El ancho total es la suma de los anchos de los miembros.

FormaUsoDescripción
&reg.FixedWidth()expresiónDevuelve la trama del struct: un char del ancho total. Usarlo como sentencia da E00306.
&reg.FromFixedWidth(&texto)sentenciaLlena el struct desde una trama (operación inversa). &texto es char/varchar o un literal. Usarlo como expresión da E00306.
FixedWidthSplit(&registros, &lineas)sentenciaConvierte una list(struct(...)) en una línea de trama por registro, dentro de una list(char)/list(varchar). Reemplaza el contenido de &lineas.
FixedWidthParse(&lineas, &registros)sentenciaOperación inversa: lee cada línea de &lineas como un registro de &registros. Reemplaza el contenido de &registros.

Formato por miembro:

TipoPosicionesFormato
char(n) / varchar(n)nAlineado a la izquierda, relleno con blancos (se recorta si sobra).
number(p,s)pAlineado a la derecha, con ceros a la izquierda y decimal implícito (sin punto). Un negativo lleva - en la primera posición, por lo que hay que dejar holgura de un dígito en p.
date8YYYYMMDD.
time6HHMMSS.
timestamp20YYYYMMDDHHMMSSnnnnnn (con microsegundos).

Al leer (FromFixedWidth, FixedWidthParse): una línea más corta que la trama se lee como si estuviera rellena con blancos; un número inválido da 0; una fecha inválida da la fecha mínima (0001-01-01).

FixedWidth() y FromFixedWidth() trabajan con UN struct; para una list use FixedWidthSplit / FixedWidthParse. Llamar a FixedWidth() sobre una list da E00305 con la sugerencia de usar FixedWidthSplit(&lista, &lineas).

FixedWidthSplit y FixedWidthParse aceptan también un camino a una list que es miembro de un struct, por ejemplo FixedWidthSplit(&pase.Items, &lineas) y FixedWidthParse(&lineas, &pase.Items). Una list miembro admite hasta 100 registros: FixedWidthParse se detiene al llenarse y emite el aviso FixedWidthParse: lista llena (max 100).

Sólo se admiten structs cuyos miembros sean char, varchar, number, date (o un field que resuelva a alguno de ellos). Un struct o list anidado, o una trama de más de 32766 posiciones, da E00305. Los elementos de la lista de líneas deben poder contener la trama completa (E00309).

Ejemplo con struct Cliente [codigo char(5) nombre varchar(12) saldo number(9,2) alta date]: la trama mide 34 posiciones (codigo 1-5, nombre 6-17, saldo 18-26, alta 27-34).

@Variables {
    global {
        struct Cliente [
            codigo  char(5)
            nombre  varchar(12)
            saldo   number(9,2)
            alta    date
        ]
        c         struct(Cliente)
        otro      struct(Cliente)
        clientes  list(struct(Cliente))
        copia     list(struct(Cliente))
        lineas    list(char(40))
        trama     char(40)
    }
}
@Source {
    &c.codigo = "A001"
    &c.nombre = "Ana Perez"
    &c.saldo = -1250.5
    &c.alta = Today()
    &trama = &c.FixedWidth()
    &otro.FromFixedWidth(&trama)
    &clientes.Add(&c)
    &clientes.Add(&otro)
    FixedWidthSplit(&clientes, &lineas)
    FixedWidthParse(&lineas, &copia)
}

Un ejemplo completo está en example/FIXEDWIDTH01.warp. Errores: E00305 a E00309 (ver Diagnósticos).

Nota: los number se leen con decimal implícito y signo - en la primera posición; las date en formato YYYYMMDD. Una trama más corta que el registro se lee como si tuviera blancos al final.

Struct: templates y acceso a miembro

Un template con nombre y miembros, definido inline dentro del ámbito global de @Variables:

@Variables {
    global {
        struct Person [
            Id        number(10, 0)
            Nombre    varchar(50)
            Email     varchar(100)
            Edad      number(3, 0)
        ]
        personaGlobal   struct(Person)
        Nombre          varchar(50)
    }
    ImprimirPersona {
        persona   struct(Person)
    }
}
&personaGlobal.Nombre = "Su Nombre"
&Nombre = &personaGlobal.Nombre
Do ImprimirPersona(&personaGlobal)
  • Los miembros del template admiten char/varchar/number/date/field(NombreCampo), struct(OtroTemplate) y list(struct(OtroTemplate)) (ver Structs anidados). time y timestamp no se admiten como miembro, ni matrix ni list de tipos escalares (E00237: “sólo se permiten char/varchar/number/date/field, struct(Template) y list(struct(Template)) (no se admite matrix ni list de tipos escalares)”).

  • Se accede/asigna un miembro con &Variable.Miembro (lectura y escritura).

  • Se puede pasar como parámetro y devolver desde una Procedure/Function propia — ver Procedure / Function.

  • Un arreglo de struct se declara con list(struct(NombreTemplate)) — ver List: métodos.

  • El indexado &Lista[i] de una list(struct(Template)) es válido, pero sólo en dos formas exactas: como lectura, el valor completo de una asignación a otra &Variable que sea struct(...) del mismo template (&otraVariable = &Lista[i]); como escritura, &Lista[i] = &otraVariable (otra &Variable struct(...) del mismo template, nunca una expresión o un literal). Si el lado de la asignación que debería ser la variable struct no es sintácticamente una &Variable, se reporta E00241; si el template no coincide exactamente con el de la lista, se reporta E00242. Los chequeos de índice (cantidad, tipo number, rango 1..9999) aplican igual que para cualquier otra list:

    &otroRegistro = &Lista[1]
    &Lista[1] = &otroRegistro
    For &i = 1 To &Lista.Count()
        &actual = &Lista[&i]
    EndFor
    
  • No soportado todavía: struct como parámetro de un programa externo (@Parameters/Call) — sólo como parámetro de una Procedure/Function interna.

Structs anidados y listas dentro de un struct

Un miembro de un template puede ser a su vez struct(OtroTemplate) (struct anidado) o list(struct(OtroTemplate)) (lista de structs). El template referenciado puede estar declarado más abajo:

struct Detalle [
    Pgm       char(10)
    Libreria  char(10)
]
struct Cabecera [
    epm       char(10)
    Estado    char(10)
    Detalle   list(struct(Detalle))
]
struct Envio [
    Cab       struct(Cabecera)
    Total     number(9, 0)
]
&env   struct(Envio)
&fila  struct(Detalle)

El acceso es por camino, con tantos niveles como haga falta:

&env.Cab.epm = "x"
&x = &env.Cab.epm
&fila.Pgm = "PGM1"
&env.Cab.Detalle.Add(&fila)
&n = &env.Cab.Detalle.Count()
&fila = &env.Cab.Detalle[1]
&env.Cab.Detalle[2] = &fila
&env.Cab.Detalle.Remove(1)
&env.Cab.Detalle.Clear()
&texto = &env.Json()
&textoDet = &env.Cab.Detalle.Json()
  • Capacidad: una list que es miembro de un struct reserva 100 elementos (no los 9999 de una list suelta). Add sobre una lista llena muestra un mensaje y no agrega el elemento, igual que en la list suelta. Un índice literal mayor que 100 se reporta como E00188.
  • Tamaño máximo: si la variable ocuparía más que el máximo de una variable RPG (16 773 104 bytes) se reporta E00300 (p.ej. una list(struct) de 9999 elementos cuyo struct lleve una lista anidada grande).
  • Ciclos: un template que se contiene a sí mismo, directa o indirectamente, se reporta como E00299.
  • Templates inexistentes: struct(X) o list(struct(X)) dentro de un miembro con un X no declarado se reporta como E00238.
  • No admitido como miembro: matrix y list de tipos escalares (E00237); tampoco time ni timestamp (E00237).
  • Procedures: se puede pasar un struct con anidados a una Procedure; si la rutina modifica una lista anidada, el parámetro se pasa por referencia (no const).
  • RPG generado: los templates se emiten en orden de dependencia; la lista miembro es un arreglo dim(100) con un contador oculto como subcampo del mismo template (se copia junto con el struct), y las variables que contienen listas se inicializan con inz(*likeds).
  • Json(): ver Json(); los anidados generan objetos y arreglos anidados.

Un ejemplo completo está en example/NSTJSON01.warp.