WARP & Warpgate: la evolución en el desarrollo para IBM i
WARP es un lenguaje de programación de alto nivel con sintaxis declarativa, diseñado para optimizar y acelerar el desarrollo en entornos IBM i (AS/400). En lugar de escribir manualmente extenso código RPGLE o definiciones DDS, WARP permite definir programas, pantallas interactivas y tablas en archivos .warp limpios y expresivos.
A través del compilador Warpgate, el código WARP se transforma automáticamente en fuente RPGLE y DDS altamente optimizado, listo para ejecutarse en el sistema.
Características principales
- Ingeniería de alto rendimiento: el compilador Warpgate está desarrollado 100% en Rust, garantizando una velocidad de compilación ultra rápida, seguridad de memoria y portabilidad nativa en Windows, GNU/Linux y macOS.
- Integración en pipelines CI/CD: al ser un ejecutable nativo multiplataforma capaz de realizar builds desde la línea de comandos, se integra fácilmente en pipelines de integración y despliegue continuo (GitHub Actions, GitLab CI, Azure DevOps, etc.), llevando prácticas DevOps modernas a la plataforma IBM i.
- Despliegue remoto multi-protocolo: permite la transferencia y despliegue directo hacia servidores IBM i remotos mediante protocolos seguros como SSH, SFTP y FTP, configurables desde el archivo de proyecto (ver
@Connection/Protocol). - Modernización y productividad: reduce drásticamente las líneas de código necesarias y elimina la verbosidad del RPG tradicional, agilizando el flujo de trabajo de los equipos de desarrollo.
- Evolución continua: mejora e incorpora constantemente nuevas capacidades de lenguaje y generación de código.
Instalación
Warpgate se distribuye como una extensión de VSCode, disponible en el Visual Studio Marketplace:
- Abrir VSCode → pestaña Extensiones (
Ctrl+Shift+X). - Buscar “WaRPGate for IBM i” (publisher Software House) e instalar.
La extensión ya incluye el compilador para Windows, GNU/Linux y macOS — no hace falta instalar nada aparte. Para generar y desplegar código (generate/build/reverse) hace falta una licencia — ver Licenciamiento.
Desarrollado por Software House
WARP y Warpgate son diseñados, desarrollados y mantenidos por Giuliano Gonzales Zeballos, creador y arquitecto principal en Software House, firma dedicada a la ingeniería de software, arquitectura de sistemas y soluciones de modernización para entornos enterprise.
Sobre este libro
Este libro documenta:
- Las guías: cómo armar, paso a paso, cada tipo de objeto (
Program,Screen,Table) y las funcionalidades más grandes (DataGridView, reportes conMember/Export). - La referencia del lenguaje: cada sección (
@Properties,@Variables,@Source, …), cada tipo de dato, cada sentencia de control y cada función builtin, con su sintaxis exacta y sus reglas de validación.
¿Qué es un archivo .warp?
Un archivo .warp describe un objeto: un Program (una lógica de negocio, con o sin salida impresa), un Screen (una pantalla interactiva 5250) o una tabla (un físico DDS). El tipo se declara en @Properties/Type.
Un .warp se organiza en secciones de nivel superior, marcadas con @Nombre { ... }:
@Documentation {
Author Name : "Nombre Programador"
}
@Properties {
Type : Program
Name : PTESTFN
Description : "Ejemplo mínimo"
}
@References {
Programs = []
Screens = []
Tables = []
}
@Variables {
global {
Usuario char(10)
}
}
@Parameters {
}
@Source {
&Usuario = USERID()
}
Qué secciones son válidas y en qué orden depende del Type — un Program tiene @Layout/@Source, una tabla tiene @Structure/@Fields/@Indexes, un Screen tiene @Layout/@Source pero no @Structure. Cada capítulo de la referencia detalla en qué tipo de archivo aplica cada sección.
Cómo compilar
El compilador es un binario CLI (cli, distribuido como warpgate/warpgate.exe dentro de la extensión de VSCode) que recibe un proyecto (.warproj) y un archivo fuente:
warpgate --project mi-proyecto.warproj --source programs/PMIPROG.warp --action validate
warpgate --project mi-proyecto.warproj --source programs/PMIPROG.warp --action generate
warpgate --project mi-proyecto.warproj --source programs/PMIPROG.warp --action build --profile Prod
validate: sólo corre el análisis semántico (lo que también dispara la extensión de VSCode al guardar) — no toca IBM i.generate: genera el RPGLE/DDS/CL y los escribe enOutput— puramente local, nunca se conecta al IBM i (aunque el CL generado ya referencia rutas remotas, según@Connection/@Deployment).build: hace lo mismo quegenerate, y además sube lo generado y lo compila en el IBM i configurado en@Connection(CRTPF/CRTLF/CRTBNDRPG/CRTDSPF, según el tipo).reverse: reconstruye un.warpde tabla a partir de un DDS ya existente en el IBM i (ingeniería inversa).
Cuando el .warpcfg declara más de un @Profile (Dev/UAT/Prod, ver Archivo de proyecto), generate/build/reverse y license-status necesitan saber cuál usar — con el flag --profile <Nombre> o, si se omite, con Default Profile de @Project. validate ignora este flag por completo.
Ver Archivo de proyecto para el formato de .warproj/.warpcfg.
Primeros pasos: tu primer Program
Un Program es la unidad básica de lógica de negocio: recibe parámetros, hace algo (leer/escribir tablas, calcular, invocar otros programas) y termina. No tiene por qué mostrar pantalla ni imprimir nada.
1. Archivo mínimo
@Documentation {
Author Name : "Tu Nombre"
}
@Properties {
Type : Program
Name : PSALUDO
Description : "Programa de saludo mínimo"
}
@References {
Programs = []
Screens = []
Tables = []
}
@Variables {
global {
Usuario char(10)
Saludo char(30)
}
}
@Parameters {
}
@Source {
&Usuario = USERID()
&Saludo = "Hola, " + &Usuario
}
Esto ya es un .warp válido: declara el objeto (@Properties), sus variables (@Variables) y su lógica (@Source). @References/@Parameters quedan vacíos porque este programa no depende de otros objetos ni recibe parámetros.
2. Agregar parámetros
Un Program recibe parámetros declarados en @Parameters, referenciando variables ya declaradas en @Variables/global:
@Variables {
global {
Fecha date
Hora char(8)
}
}
@Parameters {
inout:&Fecha,
inout:&Hora
}
Los modos son in (sólo entra), inout (entra y puede modificarse) y out (sólo sale) — ver @Parameters.
3. Invocar otro programa
Para llamar a un programa externo, agregalo a @References/Programs y usá Call(...):
@References {
Programs = ["../programs/POTRO.warp"]
}
@Source {
Call(POTRO, &Fecha, &Hora)
}
4. Procedimientos y funciones locales
Procedure/Function viven dentro de @Source, con sus propias variables en @Variables (una entrada por nombre de procedimiento/función, además de global):
@Variables {
global {
Usuario char(10)
}
Saluda {
nombre char(10)
}
}
@Source {
Function Saluda(&nombre) char(20)
Return "Hola " + &nombre
EndFunc
&Usuario = USERID()
Message("Bienvenido", Info) // sólo válido en Screen; en Program usar Print()
}
Para invocar un Procedure (sin retorno) usá Do Nombre(...); una Function (con retorno) se invoca como expresión: &variable = Nombre(...).
5. Siguiente paso
- Si tu programa necesita leer/escribir una tabla: Tablas.
- Si necesita mostrar una pantalla interactiva: Pantallas.
- Si necesita generar un reporte a un archivo de texto: Reportes con Member()/Export().
Para la sintaxis completa de cada sección, ver la Referencia del lenguaje.
Tablas: @Structure, @Fields, @Indexes
Una tabla .warp describe un físico DDS: sus columnas y sus accesos por clave. Se referencia desde un Program/Screen vía @References/Tables.
1. Archivo mínimo
@Properties {
Type : "Table"
}
@Structure {
Name : "GRIDDEMO"
Description : "Clientes de ejemplo"
@Fields {
CodCli char(6) Description("Código de Cliente")
Nombre char(20) Description("Nombre")
Ciudad char(15) Description("Ciudad")
}
@Indexes {
PrimaryKey ( GRIDDEMOPK, [CodCli] )
Index ( GRIDDMNOM, [Nombre] )
}
}
@Fields: una líneaNombre tipo(...)por columna. Sólochar/number/date— ver Tipos de dato. Modificadores disponibles:Description(...),Default(...),AllowNull.@Indexes:PrimaryKey(obligatoria, sus campos no pueden serAllowNull),Unique,Index— cada uno comoKind(Nombre, [Campo, ...], "Descripción opcional").
2. Referenciarla desde un Program
@References {
Tables = ["../tables/TLGRIDDEMO.warp"]
}
La ruta es relativa al archivo que la referencia. Una vez referenciada, sus campos quedan disponibles como identificadores sueltos dentro de un For Each/New (ver abajo), y como field(NombreCampo) en @Variables para heredar su tipo.
3. Leer filas: For Each In
@Source {
For Each In GRIDDEMO Index GRIDDMNOM
Where CodCli = &VCodCli
&VNombre = Nombre
&VCiudad = Ciudad
When None
Message("Cliente no encontrado", Error)
EndFor
}
Index selecciona el acceso (por defecto el de acceso secuencial si se omite); Where filtra por igualdad; When None corre si no hubo ninguna fila.
4. Insertar filas: New
@Source {
New In GRIDDEMO
CodCli = &VCodCli
Nombre = &VNombre
Ciudad = &VCiudad
When Duplicate
Message("Ya existe ese código", Error)
EndNew
}
When Duplicate corre si el insert choca con una clave única existente.
5. Documentación extendida
Una tabla puede llevar además @Documentation (anidada dentro de @Structure, a diferencia de un Program donde va al nivel superior) con @Author, @Application, @Module, @Repository e @History (@Creation + @Change por cada entrada del changelog) — ver @Structure, @Fields, @Indexes para el detalle completo de cada sub-sección.
6. Ingeniería inversa desde un DDS existente
Si la tabla ya existe en el IBM i, no hace falta escribirla a mano: el comando WaRPGate: Reverse Engineer de la extensión de VSCode se conecta al IBM i, lee el DDS (y opcionalmente sus LF asociadas como @Indexes) y genera el .warp correspondiente.
Pantallas: @Layout y Screen
Un Screen es una pantalla interactiva 5250: define su distribución (@Layout) y reacciona a eventos del usuario (@Source).
1. Estructura mínima
@Properties {
Type : Screen
Name : SFORM01
Description : "Pantalla de ejemplo"
Path : Screens
}
@References {
Tables = ["../tables/TLADD50.warp"]
}
@Variables {
Global {
Usuario char(10)
Fecha Date
}
}
@Parameters {
}
@Layout {
@Label(1,2,&Usuario)
@Label(1,70,&Fecha)
@Input("txtNroSolicitud", 3, 1, "Nro. Solicitud: ", &NroSolicitud, true)
}
@Source {
Event Init
&Usuario = UserId()
&Fecha = Today()
EndEvent
}
A diferencia de un Program, un Screen no tiene @Structure propia — sólo referencia tablas para leer/escribir.
2. Controles de @Layout
Label(x, y, texto): texto/variable estático.x,y = 1,1(esquina exacta) está prohibido — DDS lo rechaza.Input("Nombre", x, y, texto, &Variable, habilitado): campo editable."Nombre"es lo que devuelveCurrentInput()cuando el cursor cae ahí.habilitadoes opcional (defaulttrue): literal,&Variableo función de retornonumber.DataGridView("Nombre", x, y, filas)[...]: grilla — ver DataGridView.
Los nombres de control pueden prefijarse con @ (@Label, @Input) o no — es sólo estilo, mismo significado.
3. Eventos estándar
| Evento | Cuándo dispara |
|---|---|
Init | Una sola vez, antes del primer despliegue. |
Load | Una sola vez, antes del primer despliegue — típicamente llena el DataGridView con LoadRow(). |
Enter | Al presionar Enter (sin tecla de función activa). |
Refresh | Al presionar F5, si está declarada en @FunctionKeys del proyecto. |
Close | Fijo a F3 — siempre corre y cierra la pantalla, sin declaración necesaria. |
@Source {
Event Init
&Usuario = UserId()
EndEvent
Event Load
For Each In LADD50 Index LADD5010
Where SPT25PAIS = &nPais
&cEstado = ADSTS50
LoadRow()
EndFor
EndEvent
Event Enter
&Control = CurrentInput()
Message("Control activo: " + &Control, Info)
EndEvent
}
4. Teclas de función personalizadas
Además de los eventos estándar, se puede atar un evento a cualquier tecla F1-F24 (F13-F24 vía Shift):
@Source {
Event 'BuscarCliente' 4
Message("Se presionó F4", Info)
EndEvent
}
La etiqueta que se muestra en la línea de atención se configura en @FunctionKeys del .warpcfg del proyecto — ver Proyecto.
5. Mensajes al usuario
Message("Filas encontradas: " + String(&nFilas, 5), Info)
Error (rojo), Warning (amarillo), Info (color normal). Se muestra en el siguiente refresco y se limpia después de un ciclo.
6. Siguiente paso
Si la pantalla necesita mostrar una lista de filas con selección/paginación, ver DataGridView.
DataGridView: grillas paginadas
DataGridView muestra una lista de filas de una tabla en un Screen, con paginación nativa (Page Up/Down) sobre un subfile DDS. Sólo puede haber uno por Screen.
1. Ejemplo completo
@Properties {
Type : Screen
Name : SGRIDDEMO
}
@References {
Tables = ["tables/TLGRIDDEMO.warp"]
}
@Variables {
global {
VCodCli char(6)
VNombre char(20)
VCiudad char(15)
VTotal number(5)
VUltimo char(6)
}
}
@Layout {
DataGridView("GridDemo", 2, 2, 5)
[
Column("Codigo", &VCodCli)
Column("Nombre", &VNombre)
Column("Ciudad", &VCiudad)
]
}
@Source {
Event Load
For Each In GRIDDEMO Index GRIDDMNOM
&VCodCli = CodCli
&VNombre = Nombre
&VCiudad = Ciudad
LoadRow()
EndFor
EndEvent
Event Enter
&VTotal = 0
For Each Row
&VTotal = &VTotal + 1
&VUltimo = &VCodCli
EndFor
Message("Filas: " + String(&VTotal, 5) + " Ultimo: " + &VUltimo, Info)
EndEvent
}
2. Cargar la grilla: Event Load + LoadRow()
LoadRow() toma los valores actuales de las variables atadas a cada Column y agrega una fila a la grilla. Sólo es válido dentro de un For Each en el cuerpo de Event Load (ese For Each debe declarar Index). Carga todo el resultado en una sola pasada — Page Up/Down son scroll nativo del terminal sobre lo ya cargado; Load nunca vuelve a correr.
3. Leer la selección: RefreshSelectRow() y CurrentInput()
Cuando el cursor cae dentro del rectángulo de la grilla (encabezado, fila de atención o cualquier fila de datos), CurrentInput() devuelve el nombre de la grilla. RefreshSelectRow() relee la fila bajo el cursor y escribe cada columna (incluidas las Hidden) de vuelta en su variable atada — es la inversa de LoadRow():
Event Enter
&Control = CurrentInput()
If &Control = "GridDemo"
RefreshSelectRow()
Message("Seleccionaste: " + &VCodCli, Info)
EndIf
EndEvent
4. Recorrer todas las filas cargadas: For Each Row
For Each Row ... EndFor recorre todas las filas ya cargadas en el subfile (no la tabla de base de datos), escribiendo cada Column en su variable atada antes de cada iteración — misma dirección que RefreshSelectRow(). No admite Index/Where/When None porque no hay tabla que recorrer:
Event Enter
&VTotal = 0
For Each Row
&VTotal = &VTotal + 1
EndFor
EndEvent
5. Columnas ocultas (Hidden)
Column("ID Interno", &VIdInterno, Hidden)
Una columna Hidden carga su dato (participa en LoadRow()/RefreshSelectRow()/For Each Row) pero no muestra la celda ni el título de su encabezado — útil para llevar una clave interna sin ocupar espacio en pantalla.
6. Columnas editables (Input)
Column("Cantidad", &VCantidad, Input)
Permite que el usuario edite el valor directamente en la grilla.
7. Reglas a tener en cuenta
- El nombre (
"GridDemo") es obligatorio y único entreInput/DataGridViewde la misma pantalla. - Sólo un
DataGridViewporScreen. - Sólo válido dentro de
@Layoutde unScreen, nunca de unProgram.
Reportes con Member() / Export()
Patrón para generar un reporte: acumular filas en una tabla física (un miembro de datos exclusivo del usuario/ejecución) y, al terminar, volcarlo a un archivo de texto en el IFS.
Esta funcionalidad usa
varcharen@Variables— ver Tipos de dato.
1. Ejemplo completo
@Properties {
Type : Program
Name : PTESTME
}
@References {
Tables = ["../tables/TLT3D01.warp"]
}
@Variables {
global {
Usuario char(10)
Destino varchar(200)
T301TIPLOT char(1)
T301NROTAR char(19)
}
}
@Source {
&Usuario = USERID()
&Destino = "/tmp/reporte.txt"
Member(LT3D01, &Usuario)
For Each In LT3D01 Index LT3D0101
Where T301TIPLOT = &T301TIPLOT
Where T301NROTAR = &T301NROTAR
// ... acumula filas en LT3D01, p.ej. con New In LT3D01 ...
EndFor
Export(LT3D01, &Destino)
}
2. Member(Tabla, expr): un miembro por usuario/ejecución
Member(LT3D01, &Usuario)
Liga un miembro de datos (parametrizado por expr, típicamente &Usuario para que cada usuario tenga el suyo) a Tabla para todo el programa:
- Lo crea con
ADDPFMsi no existe todavía (tolera el error si ya existe). - Lo deja activo con
OVRDBF— efectivo para el resto del programa, incluida cualquier apertura de esa tabla y cualquierExport(...)posterior sobre la misma.
Reglas de posición (a lo sumo una vez por tabla, en cualquier caso):
- En un
Program: sólo a nivel superior de@Source(no anidado dentro deIf/While/For/Event/etc.). - En un
Screen: sólo a nivel superior deEvent Init(no anidado, y no en ningún otroEvent) —Initcorre una única vez, antes del loop principal de la pantalla, el mismo lugar donde iría el prólogo de unProgram:
@Properties {
Type : Screen
Name : SGRIDDEMO
}
@Source {
Event Init
Member(LT3D01, &Usuario)
EndEvent
Event Load
For Each In LT3D01 Index LT3D0101
// ... llena el DataGridView, o lee/escribe filas ...
EndFor
EndEvent
}
Export(...) sigue sin estar permitido en Screen — no hay caso de uso para exportar un reporte desde una pantalla interactiva.
Importante:
Member(Tabla, expr)debe aparecer antes de cualquierFor Each/NewsobreTablaen el mismo cuerpo — usarlo después de haber leído/escrito la tabla es un error en tiempo de ejecución, no de compilación.
3. Export(Tabla, destino): volcar a IFS
Export(LT3D01, &Destino)
Copia el miembro activo de Tabla (el que dejó Member(...), si hay uno) a un stream file del IFS con CPYTOIMPF, formato delimitado por coma (RCDDLM(*CRLF) STRDLM(*NONE) FLDDLM(',')), reemplazando el destino si ya existe (MBROPT(*REPLACE)).
Reglas: sólo válido en Program; a diferencia de Member, sin restricción de posición — se usa donde convenga, típicamente después del For Each/New que llenó la tabla de reporte.
Proyecto: .warproj y .warpcfg
Todo .warp se compila en el contexto de un proyecto (.warproj), que a su vez apunta a un archivo de configuración del generador (.warpcfg) con las reglas de generación de código y el despliegue al IBM i.
1. .warproj
@Project {
Name : Example
Description : "Proyecto de ejemplo"
Version : 1.0
Default Profile : Dev
@Generator {
Name : "waRPGate generator"
Language : RPGLE
Description : "Generador RPGLE"
@Paths {
Config : ../example/generator/rpg/rpg.warpcfg
Output : output/rpg
}
}
}
@Paths/Config: ruta al.warpcfg(relativa al.warproj).@Paths/Output: carpeta donde se escribe el RPGLE/DDS generado.
2. .warpcfg
@GeneratorConfig {
Date Format : DMY
Date Separator : /
Time Format : 24H
Time Separator : :
Print Mode : File
Commitment : True
Commit on Exit : True
DDS Name : <ObjectName>
@Screen {
Rows : 24
Columns : 80
@FunctionKeys {
F3 : "Salir"
F5 : "Refrescar"
F24 : "Mas teclas"
}
}
@Profile "Dev" {
@Connection {
Host : DEVSERVER
User : usuario
Auth Method : key
Key File : ~/.ssh/id_rsa_ibmi
Protocol : ssh
Port : 22
Timeout : 30
}
@Deployment {
CL Source : QCLSRC
Cleanup : true
Target Release : *Current
Optimization : 40
Debug : false
Temp Path : /tmp
Data Library : DTALIB02
Objects Library : OBJLIB01
Source Tables : QDDSSRC
Source Programs : QRPGLESRC
Compile Library : DTALIB01,DTALIB02,DTALIB03,OBJLIB01
}
}
@Profile "Prod" {
@Connection {
Host : PRODSERVER
User : usuario
Auth Method : key
Key File : ~/.ssh/id_rsa_ibmi
Protocol : ssh
Port : 22
Timeout : 30
}
@Deployment {
CL Source : QCLSRC
Cleanup : true
Target Release : *Current
Optimization : 40
Debug : false
Temp Path : /tmp
Data Library : DTALIB02
Objects Library : OBJLIB01
Source Tables : QDDSSRC
Source Programs : QRPGLESRC
Compile Library : DTALIB01,DTALIB02,DTALIB03,OBJLIB01
}
}
}
Qué controla cada bloque
- Nivel superior (
Date Format,Print Mode,Commitment,Commit on Exit,DDS Name): reglas de generación de código, aplicadas a todo el proyecto.Commitment/Commit on Exitse pueden sobreescribir por objeto en el@Propertiesde un.warppuntual. @Screen: tamaño de pantalla por defecto (24x80 si se omite) y las teclas de función (@FunctionKeys) disponibles para losScreendel proyecto — la etiqueta que se muestra en la línea de atención. UnScreenliga una tecla a un evento propio conEvent 'Nombre' <n> ... EndEvent.@Profile "Nombre": un ambiente de despliegue (Dev,UAT,Prod, el nombre es libre). Cada perfil agrupa su propia@Connection,@Deploymenty, opcionalmente,@License. Un.warpcfgdebe declarar al menos un@Profile.@Connection: cómo llega el compilador al IBM i para--action build/reverse(no aplica avalidate/generate, que son puramente locales).@Deployment: bibliotecas y member de fuente donde se sube y compila (CRTPF/CRTLF/CRTCLPGM) el código generado.
Con más de un @Profile, hace falta indicar cuál usar: con --profile <Nombre> en el CLI, o dejando Default Profile en @Project (como en el ejemplo del .warproj de arriba). La extensión de VSCode tiene su propio selector — ver Comandos de VSCode.
Ver Archivo de proyecto para la lista completa de claves y sus valores permitidos.
Licenciamiento
Las acciones generate, build y reverse del compilador exigen una licencia. La acción validate no la exige, así que la validación de fuentes (y el IntelliSense de la extensión) funciona siempre.
La licencia se gestiona automáticamente: se toma al usar el compilador y se libera al terminar, sin intervención manual.
Modos de licencia
Organización
Un servicio de licencias administra un conjunto (pool) de seats. El compilador:
- Se conecta de forma segura al servicio de licencias de tu organización (identificando al servidor con
Fingerprint, en la configuración del proyecto). ElHost/Port/Fingerprintlos entrega quien administra ese servicio — ver Configurar el servicio de licencias si esa persona eres tú. - Cada usuario ocupa un solo puesto (seat) a la vez.
- Si el compilador se cierra o pierde la conexión, el puesto se libera automáticamente — nadie queda ocupando una licencia sin usarla.
- Compatible con autenticación Kerberos/Active Directory, para integrarse con las credenciales corporativas ya existentes.
Individual
Un archivo de licencia firmado, ligado al equipo, que se valida localmente y sin red. La ruta por defecto es:
- Linux/macOS:
~/.config/warpgate/license.json - Windows:
%APPDATA%\warpgate\license.json
Se puede usar otra ruta con File (en @License), --license-file o WARPGATE_LICENSE_FILE.
Configuración
Bloque @License en el .warpcfg
Bloque opcional dentro de cada @Profile (ver Archivo de proyecto) — cada perfil (Dev/UAT/Prod) puede tener su propia configuración de licencia, para consumir licencias o servicios de licenciamiento distintos según el ambiente. Las propiedades no distinguen mayúsculas de minúsculas.
@GeneratorConfig {
@Profile "Prod" {
@License {
Host : "servidor01.dominio.local"
Port : 7443
Fingerprint : "3f2a9c41d87b05e6a1c4f0937be2d5688a1f4c0d29e7b3a65c8d1f0e4b7a9c23"
}
}
}
Con --profile/Default Profile se elige qué perfil (y por tanto qué @License) usar en cada ejecución — ver Proyecto: .warproj y .warpcfg.
| Clave | Descripción |
|---|---|
Host | Host del servicio de licencias. Si está presente, el modo es organización y File se ignora. |
Port | Puerto TCP del servicio, de 1 a 65535. Por defecto 7443. |
Fingerprint | Huella SHA-256 del certificado del servicio: 64 caracteres hexadecimales (se aceptan mayúsculas y separadores :). |
SPN | Nombre de servicio Kerberos. Por defecto warpgate-license/<host>. |
File | Ruta del archivo de licencia individual. |
Sin Host, el modo es individual.
Flags y variables de entorno
| Flag | Variable de entorno | Equivale a |
|---|---|---|
--license-host | WARPGATE_LICENSE_HOST | Host |
--license-port | WARPGATE_LICENSE_PORT | Port |
--license-fingerprint | WARPGATE_LICENSE_FINGERPRINT | Fingerprint |
--license-spn | WARPGATE_LICENSE_SPN | SPN |
--license-file | WARPGATE_LICENSE_FILE | File |
Precedencia: flag > variable de entorno > .warpcfg.
Con varios @Profile declarados, license-status también respeta --profile/Default Profile para saber de cuál @License leer — ver Proyecto: .warproj y .warpcfg.
Códigos de salida
| Código | Significado |
|---|---|
3 | No hay licencia disponible. El compilador muestra un mensaje con lo que se debe hacer. |
4 | Se perdió la licencia a mitad de la ejecución. |
130 | Ejecución interrumpida (Ctrl-C/SIGTERM). |
Solicitar una licencia
Estas acciones de soporte no toman seat ni necesitan --source; --project es opcional.
warpgate --action license-request --license-id <ID> --license-out request.json
Escribe el archivo de solicitud (--license-out es opcional; por defecto request.json) y muestra el identificador del equipo. Con el archivo de licencia recibido, se copia a la ruta por defecto (o a la indicada con File). Instalar una licencia nueva reemplaza la anterior: la anterior deja de valer por su número de serie.
Cómo obtener una licencia (sin costo, por tiempo limitado)
Actualmente Warpgate se distribuye sin costo: Software House emite licencias individuales gratuitas, válidas por 90 días y renovables. Esta política puede cambiar más adelante, sin afectar las licencias ya emitidas.
- En el equipo donde se va a usar el compilador, generar la solicitud:
warpgate --action license-request --license-id <nombre o empresa> --license-out request.json - Enviar a warpgate@softwarehouse.pe:
- El archivo
request.jsongenerado. - El identificador del equipo que el comando imprime en pantalla (necesario para emitir la licencia).
- El archivo
- Software House responde con un archivo
license.jsonfirmado, ligado a ese equipo, válido por 90 días. - Copiar ese archivo a la ruta por defecto (o a la que indiquen
--license-file/File/WARPGATE_LICENSE_FILE):- Linux/macOS:
~/.config/warpgate/license.json - Windows:
%APPDATA%\warpgate\license.json
- Linux/macOS:
- Confirmar con
warpgate --action license-status(debe mostrar el estado y la fecha de vencimiento).
Al vencer los 90 días, generate/build/reverse dejan de funcionar hasta renovar — repetir este mismo procedimiento para obtener una licencia nueva. validate sigue funcionando siempre, incluso sin licencia. Para una licencia con seats para varios usuarios (modo organización), escribir también a warpgate@softwarehouse.pe.
Para consultar el estado actual:
warpgate --action license-status
Muestra el modo, la licencia, el vencimiento y, en modo organización, los seats en uso y disponibles.
Alcance de la licencia
Una licencia cubre un producto y una versión mayor (por ejemplo, la serie 0.x). Puede ser perpetua o tener vencimiento.
Desarrollo
Las compilaciones de desarrollo aceptan WARPGATE_LICENSE=off para omitir la exigencia. Los binarios de release no lo aceptan.
Diagnósticos relacionados
| Código | Situación |
|---|---|
E00284 | Port de @License es 0. |
E00285 | Host de @License no es un host válido. |
E00286 | Fingerprint no es una huella SHA-256 válida. |
E00287 | Aviso: hay Host sin Fingerprint. |
E00288 | Aviso: Host y File juntos (File se ignora). |
Configurar el servicio de licencias (modo organización)
Esta página es para quien administra la infraestructura de tu organización (equipo de sistemas, o quien administra el servidor IBM i) — no hace falta leerla para programar en WARP. Sirve para dejar funcionando, una sola vez, el servicio que reparte las licencias entre todo el equipo de desarrollo. Quienes solo van a usar Warpgate reciben del administrador los tres datos finales (Host, Port, Fingerprint) y los colocan en su @License — ver Licenciamiento.
1. Descargar
license-service(administra el servicio): Linux · Windowslicense-gui(consulta visual, opcional — ver sección 5): Linux · Windows- Sumas de verificación: SHA256SUMS.txt
En Linux, dar permiso de ejecución: chmod +x license-service license-gui.
2. Solicitar la licencia de organización
En el servidor donde va a correr el servicio:
license-service create-request --license-id "<Nombre de tu organización>" --out request.json
license-service gen-hwid
El segundo comando muestra varios datos del equipo; copiar el valor de primary_os_id (una cadena de 32 caracteres). Enviar a warpgate@softwarehouse.pe:
- El archivo
request.json. - El valor de
primary_os_id. - Cuántos puestos (seats) simultáneos necesita tu equipo.
Software House responde con un archivo license.json para ese servidor, válido por 90 días y renovable — mismo modelo sin costo que la licencia individual, ver Licenciamiento.
3. Instalar el servicio
Windows
license-service install-license --license license.json
license-service gen-tls-cert --subject-alt-name <host-o-dominio-del-servidor>
license-service install-windows-service --bind 0.0.0.0:7443
Esto registra license-service como un servicio de Windows normal (inicio automático, se reinicia solo si falla) — se administra desde el Administrador de servicios como cualquier otro. El segundo comando muestra la huella SHA-256 en pantalla: ese valor es el Fingerprint que necesitan quienes se conecten.
Linux, como servicio con systemd (recomendado)
- Copiar el binario y crear un usuario propio para el servicio:
sudo install -m 0755 license-service /usr/local/bin/license-service sudo useradd --system --no-create-home --shell /usr/sbin/nologin warpgate-license sudo install -d -o warpgate-license -g warpgate-license -m 0700 /var/lib/license-service - Instalar la licencia y generar el certificado, como ese usuario:
Anotar la huella SHA-256 que imprime el segundo comando: ese valor es elsudo -u warpgate-license env LICENSE_SERVICE_DIR=/var/lib/license-service \ license-service install-license --license /ruta/license.json sudo -u warpgate-license env LICENSE_SERVICE_DIR=/var/lib/license-service \ license-service gen-tls-cert --subject-alt-name <host-o-dominio-del-servidor>Fingerprint. - Descargar la unidad de
systemd, copiarla y activar el servicio:sudo cp license-service.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now license-service
Con esto el servicio arranca solo al reiniciar el servidor y se reinicia solo si falla. Logs: journalctl -u license-service -f. Abrir el puerto TCP 7443 solo para la subred de los usuarios de Warpgate.
Linux, prueba rápida (sin systemd)
Para probar antes de instalar como servicio permanente:
license-service install-license --license license.json
license-service gen-tls-cert --subject-alt-name <host-o-dominio-del-servidor>
license-service run --bind 0.0.0.0:7443
Corre en primer plano; se detiene al cerrar la terminal. Útil para verificar que todo funciona antes del paso anterior.
4. Entregar los datos al equipo
Cada desarrollador coloca estos tres datos en el @License de su proyecto (ver Licenciamiento):
- Host: el servidor donde corre el servicio.
- Port:
7443(o el que se haya indicado en--bind). - Fingerprint: el que imprimió
gen-tls-cert.
Si la organización usa Active Directory, el servicio puede exigir autenticación Kerberos — consultar a Software House para habilitarlo.
5. Consultar el servicio con license-gui
license-gui es una ventana aparte, sin línea de comandos, para ver el estado de la licencia sin afectar los puestos en uso (no consume un seat): licencia activa y vencimiento, seats en uso y disponibles, y el historial de sesiones de cada usuario (incluyendo si una sesión se cerró normalmente o se perdió).
Al abrirla, pide los mismos tres datos que cualquier cliente — Host, Port, Fingerprint — más un SPN opcional si el servicio exige Kerberos. Quien administra el servicio también puede instalar una licencia nueva desde ahí mismo, sin reiniciar nada.
Estructura de un archivo .warp
Un .warp es una secuencia de secciones @Nombre { ... }. Qué secciones aplican depende de @Properties/Type:
| Sección | Program | Screen | Table |
|---|---|---|---|
@Documentation | nivel superior | nivel superior | anidada en @Structure |
@Properties | sí | sí | sí (sólo Type) |
@Structure | no | no | sí |
@Fields | no | no | anidada en @Structure |
@Indexes | no | no | anidada en @Structure |
@References | sí | sí | no |
@Variables | sí | sí | no |
@Parameters | sí | no | no |
@Layout | sí (opcional) | sí | no |
@Source | sí | sí | no |
Un .warproj/.warpcfg no describe un objeto — usan sus propias secciones de nivel superior (@Project, @GeneratorConfig) — ver Archivo de proyecto.
Sintaxis general
- Comentarios:
//hasta fin de línea. - Strings: comillas dobles o simples indistintamente (
"texto"o'texto'son lo mismo semánticamente). - Variables: siempre con
&(&Usuario); nombres de campo de tabla van sin&dentro de unFor Each/New. - Cada sección de nivel superior lleva
@; los nombres de control dentro de@Layoutpueden llevar@opcionalmente (@Label/Labelson equivalentes).
Índice de la referencia
- @Documentation
- @Properties
- @Structure, @Fields, @Indexes
- @References
- @Variables
- Tipos de dato
- @Parameters
- @Layout
- @Source: sentencias de control
- @Source: Event y Screen
- Funciones builtin
- Expresiones y operadores
- Archivo de proyecto
@Documentation
Metadatos informativos, no afectan la generación de código.
En Program/Screen (nivel superior)
Pares planos Clave : valor:
| Clave | Descripción |
|---|---|
Author Name | Nombre de quien escribió el programa. |
Author User | User ID de quien lo escribió. |
App Id | Identificador corto de la aplicación. |
App Name | Nombre visible de la aplicación. |
App Description | Descripción larga de la aplicación. |
Module Id | Identificador corto del módulo. |
Module Name | Nombre visible del módulo. |
Module Description | Descripción larga del módulo. |
Created At | Fecha/timestamp libre (p.ej. "12/06/2025"), no se parsea ni valida. |
@Documentation {
Author Name : Nombre del Programador
Author User : Codigo de Usuario
App Id : ID de la Aplicación
App Name : Nombre de la Aplicación
Module Id : ID del Módulo (si aplica)
Created At : "12/06/2025"
}
En Table (anidada dentro de @Structure)
Aquí @Documentation no tiene claves propias, sólo sub-secciones:
@Author:Name,User,Company.@Application:Id,Name,Description.@Module:Id,Name,Description.@Repository:Official,Type(p.ej.git),Branch,Version.@History: contenedor de@Creationy cualquier cantidad de@Change.@Creation:Date,Author,User,Company,Reason.@Change(repetir el bloque por cada entrada):Date,Type(por convenciónbreaking/feature/fix/security/performance/refactor/config/docs, no forzado por el compilador),Impact(por convenciónhigh/medium/low),Version,Reason,Author,User,Company.
@Structure {
Name : "GRIDDEMO"
@Documentation {
@Author {
Name : Programador
}
@History {
@Creation {
Date : "2025-06-12"
Author : Programador
Reason : "Alta inicial"
}
@Change {
Date : "2026-01-10"
Type : feature
Impact : medium
Reason : "Agrega columna Ciudad"
}
}
}
@Fields { ... }
}
@Properties
Program
| Clave | Descripción |
|---|---|
Type | El tipo de objeto que compila este archivo (Program). |
Name | Nombre del programa. |
Description | Descripción del programa. Máximo 50 caracteres: termina en el parámetro TEXT de los mandatos CL de compilación (CRTBNDRPG/CRTDSPF/CHGPFM), que tiene ese límite real en IBM i (ver E00281). |
Print Mode | Modo de impresión, para un programa tipo reporte. |
Commitment | Si este programa corre sus tablas modificadas bajo control de compromiso. Sobreescribe el Commitment de proyecto (@GeneratorConfig) sólo para este programa. Valores: True/False. |
Commit on Exit | Si este programa emite un commit final antes de terminar. Sobreescribe el de proyecto sólo para este programa; sólo efectivo si Commitment está activo. Valores: True/False. |
DDS | Nombre del DDS al que está atado este programa. |
Path | Carpeta de salida de este programa, relativa al proyecto. |
@Properties {
Type : Program
Name : PTEST01
Description : "Programa de prueba"
Commitment : True
Commit on Exit : True
Path : "./programs/"
}
Screen
Mismas claves que Program (Type con valor Screen), más 5 propias:
| Clave | Descripción |
|---|---|
Popup | Genera el DSPF de este Screen con el keyword DDS WINDOW (ventana emergente) en vez de pantalla completa. Exige las 4 claves de abajo. Valores: True/False. |
Window Row | Fila (1-based) de la esquina superior izquierda de la ventana. Requerida si Popup: True. |
Window Column | Columna (1-based) de la esquina superior izquierda de la ventana. Requerida si Popup: True. |
Window Rows | Alto de la ventana, en filas. Requerida si Popup: True. |
Window Columns | Ancho de la ventana, en columnas. Requerida si Popup: True. |
@Properties {
Type : Screen
Name : SCONFIRM
Popup : True
Window Row : 5
Window Column : 10
Window Rows : 10
Window Columns : 40
}
Un Screen con Popup: True se genera, compila y despliega exactamente igual que cualquier otro Screen — sólo cambia el DSPF (WINDOW en vez de pantalla completa). Para mostrarlo desde otro Screen, referencialo en @References/Screens e invocalo con Call(Nombre, ...) — ver @Source: sentencias de control.
Table
Sólo Type:
@Properties {
Type : "Table"
}
El resto de la descripción de una tabla va en @Structure — ver @Structure, @Fields, @Indexes.
@Structure, @Fields, @Indexes
Sólo aplican a archivos tabla (@Properties/Type : "Table").
@Structure
| Clave | Descripción |
|---|---|
Name | Nombre de la tabla. Debe empezar con letra, sólo letras/dígitos, máximo 10 caracteres (límite de nombre de objeto DDS). |
Description | Descripción de la tabla. |
Contiene además, anidadas: @Fields, @Indexes y opcionalmente @Documentation (ver @Documentation).
@Fields
Una línea Nombre tipo(...) por columna:
@Fields {
CodCli char(6) Description("Código de Cliente")
Nombre char(20) Description("Nombre")
Saldo number(9,2) Default(0)
FecAlta date
}
Tipos permitidos: sólo char, number, date (no field/list/matrix — esos son exclusivos de @Variables, ver Tipos de dato).
Modificadores:
| Modificador | Descripción |
|---|---|
Description("texto") | Descripción del campo. Requiere string entre comillas. |
Default(valor) | Valor por defecto: string entre comillas o número suelto, según el tipo del campo. No permitido en campos date. |
AllowNull | Permite NULL en este campo. Un campo PrimaryKey no puede llevar este modificador. |
@Indexes
Una entrada Kind(Nombre, [Campos...], "Descripción opcional") por acceso:
@Indexes {
PrimaryKey ( GRIDDEMOPK, [CodCli] )
Unique ( GRIDDEMOEMAIL, [Email], "Email único" )
Index ( GRIDDMNOM, [Nombre] )
}
| Kind | Descripción |
|---|---|
PrimaryKey | Acceso primario de la tabla. Sólo uno por tabla; sus campos no pueden ser AllowNull. |
Unique | Acceso secundario único. |
Index | Acceso secundario no único. |
Ejemplo completo
@Properties {
Type : "Table"
}
@Structure {
Name : "GRIDDEMO"
Description : "Clientes de ejemplo"
@Fields {
CodCli char(6) Description("Código de Cliente")
Nombre char(20) Description("Nombre")
Ciudad char(15) Description("Ciudad")
}
@Indexes {
PrimaryKey ( GRIDDEMOPK, [CodCli] )
Index ( GRIDDMNOM, [Nombre] )
}
}
@References
Lista otros archivos .warp de los que depende este Program/Screen. A diferencia de las demás secciones, usa Clave = [ "..." ] (lista entre corchetes), no Clave : valor.
| Clave | Descripción |
|---|---|
Programs | Otros Program .warp que este referencia (para Call(Nombre, ...) resuelto en tiempo de compilación). |
Screens | Screen .warp que este referencia — igual que Programs, habilita invocarlo con Call(Nombre, ...) (típicamente un Screen con @Properties/Popup: True, mostrado como ventana emergente). |
Tables | Tablas .warp que este referencia (habilita For Each In/New In/field(...) sobre ellas). |
@References {
Programs = [
"../programs/POTRO.warp"
],
Screens = [
],
Tables = [
"../tables/TLADD50.warp",
"../tables/TLGRIDDEMO.warp"
]
}
Las rutas son relativas al archivo que las declara. No aplica a archivos tabla (una tabla no referencia otros objetos).
@Variables
Declara variables dentro de un ámbito nombrado: global (visibles en todo el archivo) o el nombre de un Procedure/Function declarado en @Source (visibles sólo dentro de ese procedimiento/función, además de las global).
@Variables {
global {
Usuario char(10)
Fecha date
Saldo number(9,2)
}
Procedimiento {
contador number(3)
}
Saluda {
nombre char(10)
}
}
Una línea Nombre tipo(...) por variable — mismo formato que @Fields, pero con más tipos disponibles (ver Tipos de dato): char, varchar, number, date, field(Campo), list(tipo), matrix(filas, cols, tipo), struct(NombreTemplate).
Struct: definición de template inline
Dentro del ámbito global (sólo ahí) también se puede definir un template de struct con struct Nombre [ ... ], antes o después de instanciarlo con struct(Nombre) — ver Tipos de dato: Struct para la sintaxis completa y sus límites.
Modificador
Sólo Description("texto") está disponible en @Variables (a diferencia de @Fields, no admite Default/AllowNull):
@Variables {
global {
NroSolicitud field(ADHOJA50) Description("Número de Solicitud")
}
}
Límites de longitud/precisión (variable standalone, distinto de un campo de tabla)
| Tipo | Límite en @Fields | Límite en @Variables |
|---|---|---|
char | 1–32.766 | 1–16.773.104 |
varchar | no válido en @Fields | 1–16.773.100 |
number (precisión) | 1–30 | 1–63 |
Estos límites mayores en @Variables reflejan que una variable declarada ahí no está atada a un archivo físico DDS como sí lo está un campo de @Fields.
Tipos de dato
| Tipo | Sintaxis | Dónde | Descripción |
|---|---|---|---|
char | char(longitud) | @Fields, @Variables | Cadena de longitud fija. longitud requerida (1-32.766 en @Fields; 1-16.773.104 en @Variables). |
varchar | varchar(longitud) | sólo @Variables | Cadena de longitud variable. longitud requerida (1-16.773.100). Length(...) devuelve el contenido real en tiempo de ejecución, a diferencia de char. |
number | number(precisión, escala) | @Fields, @Variables | Numérico. precisión requerida (1-30 en @Fields; 1-63 en @Variables); escala opcional, default 0. |
date | date | @Fields, @Variables | Fecha, sin parámetros. En @Fields no puede llevar Default(...). |
time | time | @Fields, @Variables | Hora (hora/minuto/segundo), sin parámetros. En @Fields no puede llevar Default(...). |
timestamp | timestamp | @Fields, @Variables | Fecha y hora combinadas (con microsegundos), sin parámetros. En @Fields no puede llevar Default(...). |
field | field(NombreCampo) | sólo @Variables | Hereda el tipo de un campo NombreCampo de @Structure de alguna tabla referenciada. |
list | list(tipo) | sólo @Variables | Lista 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). |
matrix | matrix(filas, cols, tipo) | sólo @Variables | Grilla 2D de tamaño fijo filas x cols de tipo. Se lee/escribe un elemento con &Variable[fila, col]. |
struct | struct(NombreTemplate) | sólo @Variables | Instancia 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. |
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))
}
}
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étodo | Uso | Descripción |
|---|---|---|
Add(valor) | sentencia | Agrega 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() | sentencia | Vacía la lista (la cuenta de elementos vuelve a 0). |
Remove(indice) | sentencia | Quita el elemento en la posición indice (1-based, tipo number) y recorre los siguientes una posición hacia atrás. |
Count() | expresión | Devuelve la cantidad actual de elementos (tipo number). Sólo se puede usar dentro de una expresión, nunca como sentencia suelta. |
&Lista1.Add(10)
&Lista1.Add(20)
&Lista1.Add(30)
For &i = 1 To &Lista1.Count()
// ... &Lista1[&i] ...
EndFor
&Lista1.Remove(2)
&Lista1.Clear()
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).
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.
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/time/timestamp/field(NombreCampo)— no se puede anidar otrostruct, ni usarlist/matrixcomo miembro. -
Se accede/asigna un miembro con
&Variable.Miembro(lectura y escritura). -
Se puede pasar como parámetro y devolver desde una
Procedure/Functionpropia — ver Procedure / Function. -
Un arreglo de
structse declara conlist(struct(NombreTemplate))— ver List: métodos. -
El indexado
&Lista[i]de unalist(struct(Template))es válido, pero sólo en dos formas exactas: como lectura, el valor completo de una asignación a otra&Variableque seastruct(...)del mismo template (&otraVariable = &Lista[i]); como escritura,&Lista[i] = &otraVariable(otra&Variablestruct(...)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 reportaE00241; si el template no coincide exactamente con el de la lista, se reportaE00242. Los chequeos de índice (cantidad, tiponumber, rango 1..9999) aplican igual que para cualquier otralist:&otroRegistro = &Lista[1] &Lista[1] = &otroRegistro For &i = 1 To &Lista.Count() &actual = &Lista[&i] EndFor -
No soportado todavía:
structanidado dentro de otrostruct, nistructcomo parámetro de un programa externo (@Parameters/Call) — sólo como parámetro de unaProcedure/Functioninterna.
@Parameters
Declara los parámetros de un Program: una entrada modo:&Variable por línea. El tipo viene de la declaración correspondiente en @Variables/global, no se repite acá.
| Modo | Descripción |
|---|---|
in | Parámetro de entrada: quien llama pasa un valor, este programa no puede devolverlo modificado. |
inout | Entrada/salida: quien llama pasa un valor, y este programa puede modificarlo de vuelta. |
out | Salida: este programa lo setea, quien llama sólo lo lee de vuelta. |
@Variables {
global {
Fecha date
Hora char(8)
}
}
@Parameters {
inout:&Fecha,
inout:&Hora
}
No aplica a Screen (no recibe parámetros) ni a tablas.
@Layout
Describe la distribución de una pantalla o reporte: Label, Input, Column, DataGridView, Block, o cualquier otro Nombre(...) como control genérico. Los nombres de control pueden llevar @ opcionalmente (@Label/Label son equivalentes).
Controles con gramática propia
| Control | Sintaxis | Válido en | Descripción |
|---|---|---|---|
Label | Label(x, y, texto) | Program, Screen | Etiqueta estática en x,y. texto puede ser &Variable, "literal" o un nombre de campo suelto. x,y = 1,1 (esquina exacta) está prohibido — DDS rechaza cualquier campo ahí. |
Block | Block("Nombre")[ Label(...), ... ] | Program | Grupo nombrado e imprimible de Labels — se imprime una instancia con Print("Nombre") desde @Source. |
Input | Input("Nombre", x, y, texto, &Variable[, habilitado]) | Screen | Campo editable en x,y. "Nombre" identifica el control (se lee con CurrentInput()). &Variable es donde se guarda el valor ingresado. habilitado opcional (default true): literal, &Variable o función de retorno number (no-cero = habilitado), reevaluado antes de cada redibujado. x,y = 1,1 también prohibido. |
Column | Column(texto, variableOCampo[, Input|Hidden]) | sólo dentro de DataGridView | El 3er argumento opcional hace la columna editable (Input) o invisible (Hidden). |
DataGridView | DataGridView("Nombre", x, y, filas[, separador])[ Column(...), ... ] | Screen | Grilla nombrada en x,y mostrando filas visibles. El nombre es obligatorio y único entre Input/DataGridView. Ver DataGridView para la guía completa. |
Ejemplo (Screen)
@Layout {
@Label(1,2,&Programa)
@Label(1,70,&Fecha)
@Input("txtNroSolicitud",3,1,"Nro. Solicitud: ",&NroSolicitud,true)
@DataGridView("GridSolicitudes",6,1,8," ") [
@Column("Pais",&nPais)
@Column("Usuario",&cUsuario)
]
}
Ejemplo (Program, con Block imprimible)
@Layout {
@Block("Encabezado") [
Label(1,1,"Reporte de Ventas")
Label(2,1,&FechaHoy)
]
}
@Source {
Print("Encabezado")
}
@Layout es opcional en Program (un programa puramente batch, sin salida impresa, no la necesita) pero obligatoria en Screen. No aplica a tablas.
@Source: sentencias de control
El cuerpo imperativo de un Program/Screen: asignaciones, control de flujo, acceso a tablas, declaraciones de Procedure/Function, invocaciones y reportes.
For Each In: recorrer una tabla
For Each In Tabla [Index NombreIndice]
[Where Campo = expr]...
// cuerpo, corre por cada fila encontrada
[When None
// corre si no hubo ninguna fila
]
EndFor
Index: opcional, selecciona el acceso (por defecto el de acceso secuencial).Where: cero o más, filtra por igualdad.When None: opcional, corre si el loop no encontró ninguna fila.
Delete(): borrar la fila actual
For Each In Tabla
If &Vencido
Delete()
EndIf
EndFor
Sin argumentos — borra la fila que la iteración tiene cargada en ese momento. Sólo válido dentro de un For Each In (no dentro de un For Each Row, que recorre el subfile del DataGridView, no una tabla); válido tanto en Program como en Screen. Una fila borrada no se actualiza además, aunque el cuerpo también le haya asignado algún campo antes del Delete().
For Each Row: recorrer un DataGridView
For Each Row
// cuerpo — corre una vez por cada fila ya cargada en el subfile
EndFor
Sin Index/Where/When None (no hay tabla que recorrer). Sólo válido dentro de cualquier Event de un Screen con DataGridView. Antes de cada iteración escribe cada Column (incluidas Hidden) en su variable atada — ver DataGridView.
For clásico: contador
For &Variable = inicio To fin [Step incremento]
// cuerpo
EndFor
Cuenta desde inicio hasta fin, incrementando &Variable en incremento en cada vuelta. Step es opcional (default 1); con un Step negativo, inicio debe ser mayor que fin para recorrer hacia abajo.
Recorrer un List
No existe un For Each sobre un list en memoria (For Each In es exclusivo de tablas de base de datos, ver más abajo). Se recorre con el For clásico, usando Count() como límite y el indexado &Variable[&i]:
For &i = 1 To &Lista.Count()
// ... &Lista[&i] ...
EndFor
Ver Tipos de dato: List para la sintaxis completa de Add/Clear/Remove/Count.
New: insertar una fila
New In Tabla
Campo = expr
...
[When Duplicate
// corre si el insert choca con una clave única existente
]
EndNew
If / While
If condicion
...
Else
...
EndIf
While condicion
...
EndWhile
condicion combina comparaciones con And/Or (p.ej. &a = 1 And &b = 2 Or &c = 3). And liga más fuerte que Or (a And b Or c se lee como (a And b) Or c).
Procedure / Function
Procedure Nombre(¶m)
...
EndProc
Function Nombre(¶m) tipoRetorno
...
Return expr
EndFunc
Procedure(sin retorno,subes sinónimo): se invoca conDo Nombre(expr, ...).Function(con retorno, tipo obligatorio justo después de la lista de parámetros — misma sintaxis que@Fields/@Variables): se invoca como expresión,&variable = Nombre(expr, ...), tipado contra&variable.- Un parámetro/retorno puede ser
struct(Template)— ver Tipos de dato: Struct. El argumento pasado debe ser siempre una variable declarada con ese mismo template exacto (nunca una expresión ni un template distinto, aunque tenga los mismos miembros).
Call vs Do
| Sentencia | Uso |
|---|---|
Call(Nombre, expr, ...) | Invoca un programa o pantalla externo (uno de @References/Programs o @References/Screens), resuelto en tiempo de compilación. |
Call("Nombre", expr, ...) | Invoca un programa externo por nombre, resuelto en tiempo de ejecución. |
Do Nombre(expr, ...) | Invoca un Procedure de este mismo archivo (siempre nombre sin comillas). |
Un Screen referenciado en @References/Screens se invoca con Call(Nombre, ...) exactamente igual que un Program — típicamente para mostrarlo como ventana emergente (ver @Properties). El Screen invocado puede declarar @Parameters igual que un Program.
Print("NombreBlock")
Imprime una instancia de un Block de @Layout — ver @Layout. Escribe líneas de 132 caracteres al spool de QPRINT (OVRPRTF + open al inicio, close al final); con Print Mode: Screen el spool además se muestra (DSPSPLF) y se borra (DLTSPLF) al terminar el programa.
Member / Export: reportes con miembros de datos
Member(Tabla, expr)
Export(Tabla, destino)
Ver la guía completa: Reportes con Member()/Export().
Member(Tabla, expr): liga un miembro de datos aTablapara todo el programa (ADDPFM+OVRDBF, antes de cualquieropen). EnProgram, sólo a nivel superior de@Source; enScreen, sólo a nivel superior deEvent Init(ver @Source: Event y Screen). A lo sumo una vez por tabla.Export(Tabla, destino): copia el miembro activo deTablaa un stream file del IFS (CPYTOIMPF, delimitado por coma). Sólo válido enProgram(no enScreen); sin restricción de posición dentro de él.
Return
Return [expr]
Sale de un Function (el valor es obligatorio si se quiere honrar el tipo de retorno declarado).
@Source: Event y Screen
Sentencias exclusivas de archivos Screen.
Event
Event NombreEstandar
...
EndEvent
Event 'Etiqueta' NumeroTecla
...
EndEvent
Dos formas: evento estándar (nombre bare, sin comillas) o evento personalizado atado a una tecla de función (nombre entre comillas + número de tecla, 1-24, es decir F1-F24; F13-F24 vía Shift).
Eventos estándar
| Evento | Cuándo dispara |
|---|---|
Enter | Al presionar Enter (sin tecla de función activa). CurrentInput() identifica qué Input tenía foco, decodificado de la posición del cursor tras EXFMT. |
Close | Fijo a F3 en el DSPF generado (CF03) — siempre corre (incluso vacío) y termina la pantalla; no requiere declaración para atar la tecla. |
Load | Una sola vez, antes del primer despliegue — típicamente contiene el For Each/LoadRow() que llena el DataGridView. Page Up/Down son scroll nativo sobre lo ya cargado; este evento nunca vuelve a correr. |
Refresh | Al presionar F5, si F5 está declarada en @FunctionKeys del .warpcfg y no está tomada por un Event 'Nombre' 5 personalizado. |
Init | Una sola vez, la primera vez que arranca esta pantalla, antes del primer despliegue. No hace nada si no se declara. Único lugar de un Screen donde Member(...) es válido — ver Reportes con Member()/Export(). |
Eventos personalizados
Event 'BuscarCliente' 4
Message("Se presionó F4", Info)
EndEvent
La etiqueta mostrada en la línea de atención de la pantalla se configura en @FunctionKeys del .warpcfg del proyecto (ver Proyecto), no en el propio Event.
Message
Message(texto, Error|Warning|Info)
Muestra un mensaje en la línea de mensaje de la pantalla: Error (rojo), Warning (amarillo), Info (color normal de texto). Se muestra en el siguiente refresco de pantalla y se limpia después de un ciclo.
LoadRow / RefreshSelectRow / For Each Row
Exclusivos de pantallas con DataGridView — ver la guía completa: DataGridView.
| Sentencia | Uso |
|---|---|
LoadRow() | Sin argumentos. Toma los valores actuales de las variables atadas a cada Column y agrega una fila a la grilla. Sólo válida dentro de un For Each en el cuerpo de Event Load (ese For Each debe declarar Index). |
RefreshSelectRow() | Sin argumentos. Inversa de LoadRow(): relee la fila bajo el cursor y escribe cada columna (incluidas Hidden) de vuelta en su variable atada. Válida en cualquier Event de un Screen con DataGridView. |
For Each Row ... EndFor | Recorre todas las filas ya cargadas en el subfile (no la tabla), escribiendo cada Column en su variable atada antes de cada iteración. Sin Index/Where/When None. |
CurrentInput
&Control = CurrentInput()
Devuelve el Nombre del Input/DataGridView (de @Layout) que tiene el foco actualmente. Para un DataGridView, coincide si el cursor cae en cualquier parte de su rectángulo (encabezado, fila de atención o cualquier fila de datos), no sólo en una celda exacta. Sólo válida dentro del cuerpo de un Event — usarla en cualquier otro lugar es un error.
Funciones builtin
Usables como expresión en cualquier lugar, p.ej. &Usuario = USERID(). La mayoría son de cero argumentos con tipo de retorno fijo; Val() es la excepción (ver su propia entrada).
| Función | Retorno | Descripción |
|---|---|---|
USERID() | char(10) | Perfil de usuario IBM i que ejecuta este programa. Sin argumentos. |
PGNAME() | char(10) | Nombre de objeto de este mismo programa — constante de compilación, no una consulta en tiempo de ejecución. Sin argumentos. |
TODAY() | date | Fecha actual del sistema. Sin argumentos. |
TIME() | char(8) | Hora actual del sistema formateada "HH:MM:SS" (8 caracteres). Sin argumentos. |
Val(&CharVariable) | igual al destino de la asignación | Parsea un valor char a Number, usando la precisión/escala del destino de la asignación — a diferencia de las demás builtin, no tiene tipo de retorno fijo. Sólo válida directamente como &NumberVariable = Val(&CharExpr); usarla en cualquier otro lugar (anidada en otra expresión, como argumento de Do/Call, etc.) es un error. |
CurrentInput() | char(30) | Ver @Source: Event y Screen. |
String(&NumberVariable, integerDigits[, decimalDigits]) | char(integerDigits [+ 1 + decimalDigits]) | Convierte un Number a char, p.ej. para concatenarlo en un Message(...) (que sólo acepta texto). integerDigits/decimalDigits deben ser literales enteros (constantes de compilación) — el ancho del retorno depende de ellos. decimalDigits es opcional (default 0, sin separador decimal en el resultado). Ancho fijo, rellenado con ceros (sin supresión de ceros a la izquierda); no maneja negativos de forma especial. |
SubString(&Variable, inicio, longitud) | char(longitud) | Recorta un pedazo de ancho fijo de un valor char (variable o literal) para que quepa en una variable más chica — p.ej. &Corto40 = SubString(&Largo120, 1, 40). Sin esto, asignar un char más ancho directo a uno más angosto es error de compilación. inicio/longitud deben ser literales enteros. Cuando el tamaño del origen se conoce en tiempo de compilación, que inicio + longitud - 1 lo exceda también es error de compilación. |
Trim(&Variable) | igual al argumento (char/varchar) | Quita blancos a izquierda y derecha. Un solo argumento. |
LTrim(&Variable) | igual al argumento | Quita blancos a la izquierda. Un solo argumento. |
RTrim(&Variable) | igual al argumento | Quita blancos a la derecha. Un solo argumento. |
Length(&Variable) | number | Tamaño de un valor char/varchar: para varchar, el contenido real en tiempo de ejecución; para char, su longitud fija declarada. Un solo argumento. |
IndexOf(needle, &haystack) | number | Busca needle dentro de haystack y devuelve la posición cruda (1-based si se encuentra, 0 si no), sin conversión aplicada. Exactamente dos argumentos (texto a buscar, texto donde buscar). |
Fecha, hora y timestamp
Year/Month/Days/Hour/Minute/Second son polimórficas: aceptan date/time/timestamp según corresponda (Year/Month/Days con date o timestamp; Hour/Minute/Second con time o timestamp) — no hace falta un nombre distinto por tipo.
format (donde aplica) es siempre opcional: si se omite, cae al valor configurado en @GeneratorConfig (Date Format/Time Format/Timestamp Format); si tampoco hay configuración, se usa el formato por defecto del sistema.
| Función | Retorno | Descripción |
|---|---|---|
Year(&Variable) | number(4) | Año de un date/timestamp. |
Month(&Variable) | number(2) | Mes de un date/timestamp. |
Days(&Variable) | number(2) | Día del mes de un date/timestamp. |
Hour(&Variable) | number(2) | Hora de un time/timestamp. |
Minute(&Variable) | number(2) | Minuto de un time/timestamp. |
Second(&Variable) | number(2) | Segundo de un time/timestamp. |
DateDiff(&Fecha1, &Fecha2, Unidad) | number | Diferencia entre dos date, en Days/Months/Years (palabra suelta, no string). Positivo cuando Fecha1 es posterior a Fecha2, negativo en caso contrario. |
TimeDiff(&Hora1, &Hora2, Unidad) | number | Igual que DateDiff, sobre time, con Hours/Minutes/Seconds. |
TimestampDiff(&Ts1, &Ts2, Unidad) | number | Igual que DateDiff, sobre timestamp, con las 6 unidades habilitadas: Seconds/Minutes/Hours/Days/Months/Years. |
DateAdd(&Fecha, cantidad, Unidad) | date | Suma (o resta, si cantidad es negativa) días/meses/años a una fecha. cantidad puede ser cualquier expresión number (no sólo literal). |
TimeAdd(&Hora, cantidad, Unidad) | time | Igual que DateAdd, sobre time, con Hours/Minutes/Seconds. |
TimestampAdd(&Ts, cantidad, Unidad) | timestamp | Igual que DateAdd, sobre timestamp, con las 6 unidades. |
IsDate(valor[, format]) | number(1) | Valida si valor (char/varchar/number) es una fecha válida en format (opcional — uno de Iso/Usa/Eur/Jis/Mdy/Dmy/Ymd/Jul). Devuelve 1/0 (WARP no tiene tipo booleano). No es una expresión pura — igual que Val(), sólo es válida directamente como &NumberVariable = IsDate(...). |
IsTime(valor[, format]) | number(1) | Igual que IsDate, sobre time (format uno de Hms/Iso/Usa/Eur/Jis). |
IsTimestamp(valor[, format]) | number(1) | Igual que IsDate, sobre timestamp (format uno de Iso/Usa/Eur/Jis). Iso es el formato recomendado y de mayor compatibilidad. |
StringToDate(string[, format]) | date | Convierte char/varchar a date. |
NumberToDate(number[, format]) | date | Convierte number a date — mismas reglas de format que StringToDate. |
StringToTime(string[, format]) | time | Convierte char/varchar a time. |
StringToTimestamp(string[, format]) | timestamp | Convierte char/varchar a timestamp. |
DateToString(&Fecha[, format]) | char(10) | Convierte date a texto — mismas reglas de format. |
TimeToString(&Hora[, format]) | char(8) | Convierte time a texto. |
TimestampToString(&Ts[, format]) | char(26) | Convierte timestamp a texto (26 = ancho ISO con microsegundos). |
Now() | timestamp | Timestamp actual del sistema. Sin argumentos. |
Ejemplos
&Usuario = USERID()
&Programa = PGNAME()
&FechaHoy = TODAY()
&HoraActual = TIME()
&Cantidad = Val(&CantidadTexto)
Message("Total: " + String(&Total, 9, 2), Info)
&Corto = SubString(&Largo, 1, 40)
&Limpio = Trim(&ConEspacios)
&Tam = Length(&Descripcion)
&Pos = IndexOf("@", &Email)
&FechaVencimiento = DateAdd(&FechaHoy, 30, Days)
&DiasHastaVencer = DateDiff(&FechaVencimiento, &FechaHoy, Days)
&EsFechaValida = IsDate(&TextoFecha, Dmy)
&Ahora = Now()
&HorasTranscurridas = TimestampDiff(&Ahora, &Inicio, Hours)
Expresiones y operadores
Operadores aritméticos/concatenación
| Operador | Uso |
|---|---|
+ | Suma numérica entre Number, o concatenación entre char/varchar (según el tipo de los operandos). |
- | Resta numérica. |
* | Multiplicación numérica. |
/ | División numérica. |
&Total = &Precio * &Cantidad
&Saludo = "Hola, " + &Usuario
Operadores de comparación
=, <>, <, <=, >, >= — usados en Where, If, While.
Operadores lógicos
And/Or combinan comparaciones (típicamente en If/While): And liga más fuerte que Or.
If &a = 1 And &b = 2 Or &c = 3
...
EndIf
WARP no tiene un tipo booleano propio: cada operando se valida como cualquier otra expresión.
Literales
- Números:
100,9.5. - Texto:
"texto"o'texto'(equivalentes). - Fecha/hora: se obtienen con
TODAY()/TIME(), no hay literal de fecha.
Variables y campos
- Variable: siempre con
&(&Usuario). - Campo de tabla: sin
&, sólo dentro de unFor Each/Newsobre esa tabla (CodCli,Nombre). - Elemento de
list/matrix:&Variable[índice]/&Variable[fila, col](1-based). - Miembro de
struct:&Variable.Miembro(lectura y asignación) — ver Tipos de dato: Struct. - Método de
list:&Variable.Metodo(...)(Add/Clear/Removecomo sentencia,Count()como expresión) — ver Tipos de dato: List.
Llamadas a función/procedimiento como expresión
&variable = NombreFuncion(expr, ...)
Ver Funciones builtin y @Source: sentencias de control para Functions propias.
Archivo de proyecto (.warproj / .warpcfg)
Ver también la guía: Proyecto.
.warproj — @Project
| Clave | Descripción |
|---|---|
Name | Nombre del proyecto. |
Description | Descripción del proyecto. |
Version | Versión del proyecto (string libre, p.ej. 1.0). |
Default Profile | Nombre del @Profile (definido en el .warpcfg, ver más abajo) que usan generate/build/reverse/license-status cuando el CLI no recibe --profile. Opcional si el .warpcfg sólo declara un @Profile. |
@Generator (anidada en @Project)
| Clave | Descripción |
|---|---|
Name | Nombre del generador. |
Description | Descripción del generador. |
Language | Lenguaje de destino. Valores: RPGLE, RPG. |
@Paths (anidada en @Generator)
| Clave | Descripción |
|---|---|
Config | Ruta al .warpcfg de este proyecto. |
Output | Carpeta donde se escribe el DDS/RPG generado. |
.warpcfg — @GeneratorConfig
| Clave | Descripción | Valores |
|---|---|---|
Date Format | Formato de fecha nativo con el que se declara cada variable Date (p.ej. date(*dmy/) en vez de date a secas). | ISO, USA, EUR, JIS, MDY, DMY, YMD, JUL |
Date Separator | Carácter separador de fecha (p.ej. / en date(*dmy/)). Un solo carácter. | — |
Time Format | Formato de hora nativo con el que se declara cada variable Time (p.ej. time(*hms:) en vez de time a secas), y default para IsTime/StringToTime/TimeToString cuando se omite su argumento format. Usa es 12 horas con sufijo AM/PM, sin segundos — estructuralmente distinto de los otros 4 (24 horas hh:mm:ss). | Hms, Iso, Usa, Eur, Jis |
Time Separator | Carácter separador de hora (p.ej. : en time(*hms:), también usado por TIME()). Un solo carácter. No aplica a Usa (sin separador entre campos). | — |
Timestamp Format | Default para IsTimestamp/StringToTimestamp/TimestampToString cuando se omite su argumento format. El tipo timestamp no admite un formato en su declaración, a diferencia de date/time. Se recomienda usar Iso por su mayor compatibilidad. | Iso, Usa, Eur, Jis |
Print Mode | Qué pasa con el spool QPRINT que escribe cada Print("Block"): con Screen el programa lo muestra (DSPSPLF) y lo borra al salir; Printer/File sólo lo dejan spooleado. Default Printer. | Printer, File, Screen |
Commitment | Habilita control de compromiso para todo el proyecto: los programas generados declaran sus tablas modificadas commit usropn y las abren tras un STRCMTCTL best-effort (las tablas deben estar journaled). Un Program puede sobreescribirlo con @Properties/Commitment. Default False. | True, False |
Commit on Exit | Si los programas generados emiten un commit final antes de terminar. Sobreescribible por @Properties/Commit on Exit. Sólo efectivo si Commitment es True. Default False. | True, False |
DDS Name | Patrón para nombrar el DDS del display file de cada Screen a partir de su nombre de objeto — p.ej. <ObjectName>D agrega una D. Default <ObjectName> (sin cambio). Sólo afecta el DSPF; el programa RPG driver generado mantiene el nombre de objeto tal cual. El resultado siempre se pasa a mayúsculas y trunca a 10 caracteres (límite de nombre de objeto OS/400) — las variantes que agregan D truncan a 9 primero para que la D nunca sea lo que se trunca. | <ObjectName>, <ObjectName>D, D<ObjectName>, D<ObjectNameWithoutFirstChar> |
Las claves de nivel superior (Date Format, Print Mode, Commitment, etc.), @Screen/@FunctionKeys y los formatos de fecha/hora/timestamp son únicos a nivel de proyecto: no se repiten por perfil.
@Profile (anidada en @GeneratorConfig)
Un .warpcfg declara uno o más perfiles de despliegue (típicamente Dev, UAT, Prod, aunque el nombre es libre) dentro de @GeneratorConfig. Cada @Profile "Nombre" agrupa su propia @Connection, @Deployment y @License — así un mismo proyecto puede apuntar a servidores/bibliotecas/licencias distintos según el ambiente, sin tener que mantener varios .warpcfg.
@GeneratorConfig {
@Profile "Dev" {
@Connection { ... }
@Deployment { ... }
}
@Profile "Prod" {
@Connection { ... }
@Deployment { ... }
@License { ... }
}
}
Es obligatorio declarar al menos un @Profile: @Connection/@Deployment/@License sueltos directamente dentro de @GeneratorConfig (sin envolverlos en @Profile) ya no son válidos (error E00290). Un @GeneratorConfig sin ningún @Profile da error E00291.
Cómo se elige el perfil activo para generate/build/reverse/license-status (la acción validate ignora los perfiles):
- El flag
--profile <Nombre>del CLI, sin distinguir mayúsculas. - Si se omite,
Default Profilede@Project(sección.warproj — @Projectmás arriba). - Si tampoco hay
Default Profilepero el.warpcfgsólo declara un@Profile, se usa ese. - Si nada de lo anterior aplica y hay varios perfiles declarados, el compilador falla con error
E00293, listando los perfiles disponibles.
Pedir un perfil que no existe (--profile o Default Profile) falla con error E00292, también con la lista de perfiles disponibles.
@Screen (anidada en @GeneratorConfig)
| Clave | Descripción |
|---|---|
Rows | Cantidad de filas de pantalla. Numérico, default 24. |
Columns | Cantidad de columnas de pantalla. Numérico, default 80. |
@FunctionKeys (anidada en @Screen)
Una entrada F<n> : "Etiqueta" por línea (n de 1 a 24, p.ej. F3 : "Salir"). Se ata a un evento propio con Event 'Nombre' <n> ... EndEvent en el @Source de un Screen.
@Connection (anidada en @Profile)
Cómo llegar al IBM i:
| Clave | Descripción | Valores |
|---|---|---|
Host | Hostname o IP del IBM i. Requerida. | — |
User | Perfil de usuario para conectar. Requerida. | — |
Auth Method | Cómo autenticar. Requerida. password lee la contraseña de la variable de entorno WARPGATE_PASSWORD; interactive, al correr desde la extensión de VSCode, la pide con un cuadro de diálogo en cada Build & Deploy (corriendo el binario directo por consola, se comporta igual que password). | key, password, interactive |
Key File | Ruta al archivo de clave privada. Requerida sólo si Auth Method: key. | — |
Protocol | Protocolo de transporte. Requerida. ftp no puede combinarse con Auth Method: key. | ssh, sftp, ftp |
Port | Puerto TCP. Numérico, default 22. | — |
Timeout | Timeout de conexión en segundos. Numérico (-128 a 127), default 30. | — |
ssh/sftp/ftp usan clientes nativos: no hace falta tener instalado ningún cliente ssh/scp/sftp/ftp en la máquina donde corre el compilador.
Ejemplos por protocolo y método de autenticación
SSH o SFTP con llave (ssh/sftp son equivalentes: el cliente nativo usa el mismo transporte para ambos):
@Connection {
Host : MIHOST
User : MIUSUARIO
Auth Method : key
Key File : ~/.ssh/id_ed25519_ibmi
Protocol : ssh
Port : 22
Timeout : 30
}
SSH o SFTP con contraseña — la contraseña no se escribe en el .warpcfg; se lee de la variable de entorno WARPGATE_PASSWORD, que debe existir antes de abrir VSCode (o la terminal desde la que se corra el compilador):
@Connection {
Host : MIHOST
User : MIUSUARIO
Auth Method : password
Protocol : sftp
Port : 22
Timeout : 30
}
FTP con contraseña — FTP no tiene concepto de autenticación por llave (Auth Method: key con Protocol: ftp es un error de validación), así que sólo admite password/interactive, ambos leídos de la misma variable WARPGATE_PASSWORD:
@Connection {
Host : MIHOST
User : MIUSUARIO
Auth Method : password
Protocol : ftp
Port : 21
Timeout : 30
}
Generar el archivo de Key File
Para Auth Method: key hace falta un par de llaves SSH: una privada (la que apunta Key File) y una pública, que debe quedar autorizada en el perfil de usuario del IBM i.
- Generar el par de llaves (en la máquina donde corre el compilador/VSCode, no en el IBM i):
Esto creassh-keygen -t ed25519 -f ~/.ssh/id_ed25519_ibmi -N ""id_ed25519_ibmi(privada — la ruta que va enKey File) yid_ed25519_ibmi.pub(pública).-N ""deja la llave sin passphrase, requisito paraAuth Method: key. - Copiar el contenido de
id_ed25519_ibmi.pub(una sola línea) al final del archivo.ssh/authorized_keysdentro del directorio home IFS del usuario del IBM i (p.ej./home/MIUSUARIO/.ssh/authorized_keys) — crear el archivo y el directorio si no existen. El demonio SSH del IBM i (SSHD, del producto 5733-SC1) tiene que estar activo y configurado para aceptar autenticación por llave. - Verificar que
Key Fileen@Connectionapunte a la ruta de la llave privada en la máquina local (no la.pub), y queAuth Methodseakey.
@Deployment (anidada en @Profile)
Dónde/cómo desplegar y compilar en el IBM i:
| Clave | Descripción | Valores |
|---|---|---|
CL Source | Nombre del member de fuente CL generado (nombre de objeto OS/400, máx. 10 caracteres). Requerida. | — |
Cleanup | Si se elimina el fuente staged tras compilar exitosamente. | true, false |
Target Release | Release de OS/400 destino de la compilación (p.ej. V7R4M0). | — |
Optimization | Nivel de optimización del compilador. | — |
Debug | Si se compila con la vista de debug habilitada. | true, false |
Temp Path | Directorio staging del IFS usado durante el deploy. Requerida. | — |
Data Library | Biblioteca donde se crean los objetos físico/lógico de DDS compilados (nombre de objeto OS/400). Requerida. | — |
Objects Library | Biblioteca que contiene los members de fuente DDS/CL (QDDSSRC/QCLSRC) y donde se crea el objeto programa CL compilado (nombre de objeto OS/400). Requerida. | — |
Source Tables | Member de fuente que contiene el fuente DDS de físicos/lógicos (nombre de objeto OS/400). Requerida. | — |
Source Programs | Member de fuente que contiene el RPGLE/CL generado (nombre de objeto OS/400). | — |
Compile Library | Lista separada por comas de bibliotecas agregadas a la lista de bibliotecas del job de compilación, en orden, en vez de sólo Data Library — para programas cuyas tablas viven en varias bibliotecas. | — |
@License (anidada en @Profile)
Bloque opcional con la configuración de licenciamiento, propio de cada perfil — así Dev/UAT/Prod pueden consumir licencias o servicios de licenciamiento distintos. Las claves no distinguen mayúsculas. Ver la guía Licenciamiento.
| Clave | Descripción | Valores |
|---|---|---|
Host | Host del servicio de licencias. Con Host el modo es organización y File se ignora; sin Host, el modo es individual. | — |
Port | Puerto TCP del servicio. Por defecto 7443. | 1-65535 |
Fingerprint | Huella SHA-256 del certificado del servicio (acepta : y mayúsculas). | 64 hex |
SPN | Nombre de servicio Kerberos. Por defecto warpgate-license/<host>. | — |
File | Archivo de licencia individual. Por defecto ~/.config/warpgate/license.json (%APPDATA%\warpgate\license.json en Windows). | — |
@License {
Host : "servidor01.dominio.local"
Port : 7443
Fingerprint : "3f2a9c41d87b05e6a1c4f0937be2d5688a1f4c0d29e7b3a65c8d1f0e4b7a9c23"
}
Ejemplo completo
Ver Proyecto: .warproj y .warpcfg para un ejemplo completo con ambos archivos.
Diagnósticos y mensajes de error
El compilador reporta cada problema con un código E##### (p.ej. E00221), un nivel de severidad y un mensaje en español con los valores concretos interpolados.
Niveles
| Nivel | Significado |
|---|---|
error | Impide la generación de código; el objeto no compila. |
warning | No impide compilar, pero señala algo probablemente no intencional (p.ej. una variable declarada y nunca usada). |
Cómo se ven
Por línea de comandos (formato humano):
[2026-07-24T21:26:47Z] ERROR E00221 (line: 12, column: 5): La variable 'x' declara char(16773105), fuera del rango permitido (1-16773104).
O en JSON (--format json, el que consume la extensión de VSCode para pintar los subrayados rojos/amarillos en el editor):
{"errors": [{"line": 12, "column": 5, "severity": "error", "message": "..."}]}
Algunos ejemplos representativos
| Código | Situación |
|---|---|
E00001 | Se esperaba una sección @Nombre y se encontró otra cosa. |
E00128 | Un archivo referenciado por el .warpcfg (p.ej. la clave SSH de @Connection) no existe en esta máquina — no bloquea validate/generate, pero hará falta para build/reverse. |
E00152 | Una variable de @Variables fue declarada pero nunca usada (warning). |
E00195 | Uso inválido de RefreshSelectRow()/For Each Row fuera de un Screen con DataGridView. |
E00221 | char fuera del rango permitido para una variable standalone en @Variables (1-16.773.104). |
E00222 | number con precisión fuera de rango (1-63) en @Variables. |
E00223 | number cuya escala es mayor que su precisión. |
E00224 | varchar fuera del rango permitido (1-16.773.100). |
E00238 | struct(Nombre) referencia un template que no existe en @Variables. |
E00240 | Acceso &Variable.Miembro a un miembro que no existe en el template del struct. |
E00242 | Se pasó una variable struct(OtroTemplate) donde se esperaba un template distinto (la comparación es por nombre exacto, no por estructura). |
E00269 | La Description de una tabla (@Structure) supera los 50 caracteres que admite TEXT en DDS. |
E00270 | La Description de un campo (@Fields) supera los 50 caracteres que admite TEXT en DDS. |
E00271 | La Description de un índice (@Indexes) supera los 50 caracteres que admite TEXT en DDS. |
E00281 | La Description de @Properties (de un Program o Screen) supera los 50 caracteres que admite TEXT en los mandatos CL de compilación (CRTBNDRPG/CRTDSPF/CHGPFM). Mismo límite y criterio que E00269/E00270/E00271. |
E00284 | Port de @License es 0. |
E00285 | Host de @License no es un host válido. |
E00286 | Fingerprint de @License no es una huella SHA-256 válida. |
E00287 | Aviso: @License tiene Host pero no Fingerprint. |
E00288 | Aviso: @License tiene Host y File juntos (File se ignora). |
E00290 | @Connection/@Deployment/@License declarados directamente dentro de @GeneratorConfig, sin envolverlos en un @Profile "Nombre". |
E00291 | @GeneratorConfig no declara ningún @Profile. |
E00292 | El perfil pedido con --profile o Default Profile no existe entre los @Profile declarados (se listan los disponibles). |
E00293 | Hay varios @Profile declarados y no se indicó cuál usar (ni --profile ni Default Profile), así que el compilador no puede elegir uno solo (se listan los disponibles). |
Cada mensaje tiene un código, un nivel (error/warning) y una plantilla con los datos concretos del caso ({variable}, {length}, etc.) que el compilador completa al reportarlo.
Cuándo se disparan
- Al guardar/abrir un
.warpen VSCode (warpgate.validateOnSave/validateOnOpen), si hay un.warprojresuelto para ese archivo. - Manualmente con
warpgate --action validate— no toca IBM i, es puramente local. - Nota de licencia:
validateno exige licencia;generate/build/reversesí (ver Licenciamiento). - Como parte de
generate/build/reverse— si hay errores, no se llega a generar/desplegar código.
IntelliSense en VSCode
La extensión WaRPGate para VSCode agrega soporte de lenguaje para archivos .warp:
- Resaltado de sintaxis: secciones, keywords, tipos de dato, nombres de evento, llamadas a función, operadores.
- Autocompletado (
@/&como triggers): secciones, keywords de@Source, tipos de dato, funciones builtin (con snippet), claves de propiedad por sección, controles de@Layout. - Hover: la misma documentación de este libro, resumida en una línea, al pasar el cursor sobre cualquier keyword/función/tipo.
- Diagnósticos en vivo: valida al abrir/guardar (ver Diagnósticos), pintando subrayados rojos/amarillos directamente en el editor.
- Outline/breadcrumbs: estructura de secciones y
Procedure/Functioncomo símbolos navegables. - Ir a definición y links de documento: saltar a la tabla/programa/pantalla referenciado en
@References. - Layout Preview: vista previa en vivo de
@Layoutcomo una pantalla de terminal IBM i 5250 (24x80 por defecto, o el tamaño configurado en@Screendel.warpcfgdel proyecto). - Comandos: ver Comandos de VSCode para un ejemplo con captura de cada uno (
Validate File,Generate,Build & Deploy,Select Project File,Reverse Engineer DDS,Preview Layout,Select Layout Block).
El IntelliSense se mantiene sincronizado con cada nueva versión del lenguaje, de modo que el resaltado, el autocompletado y los diagnósticos siempre reflejan las capacidades vigentes del compilador.
Instalación
El .vsix empaquetado incluye los binarios del compilador para las 3 plataformas — no hace falta instalar el compilador por separado.
Comandos de VSCode
La extensión WaRPGate agrega 8 comandos a la paleta de comandos (Ctrl+Shift+P / F1), todos con el prefijo WaRPGate:. Esta página muestra un ejemplo real de cada uno, con capturas de pantalla.
WaRPGate: Validate File
Corre el compilador en modo validación sobre el archivo .warp activo: revisa la sintaxis y la semántica (tipos, referencias, variables no declaradas, etc.) sin generar ningún archivo. Los errores y warnings aparecen en el canal de salida “WaRPGate”.
Ejemplo: un Program que asigna una variable no declarada.
@Source {
&CodigoCliente = &Inexistente
}
WaRPGate: Generate
Corre el compilador completo sobre el archivo activo: valida y, si no hay errores, genera los archivos RPGLE/DDS/CL localmente (no despliega nada a IBM i). Es el equivalente a --action generate del CLI.
WaRPGate: Build & Deploy
Genera igual que el comando anterior y además sube los archivos al IBM i configurado en @Connection del .warpcfg, para compilarlos ahí (CRTPF/CRTLF/CRTBNDRPG/CRTDSPF). Como esto se conecta a un sistema real, siempre pide confirmación antes de continuar.
WaRPGate: Select Project File (.warproj)
Cuando el workspace tiene más de un .warproj, este comando elige cuál usar para validar/generar/desplegar. El proyecto elegido queda recordado para los demás comandos.
WaRPGate: Reverse Engineer DDS…
Reconstruye un archivo .warp de tabla a partir de un miembro DDS que ya existe en IBM i. Pide, en orden: la ruta QSYS.LIB del miembro DDS de la tabla, los miembros DDS de los índices (LF) a incluir en @Indexes (opcional), y el nombre del .warp a generar. Al final confirma antes de conectarse.
WaRPGate: Preview Layout
Muestra el @Layout del archivo activo como una pantalla de terminal IBM i 5250 (ver IntelliSense en VSCode). Si el Screen usa Popup: True, el preview dibuja la ventana en su posición y tamaño reales dentro de la pantalla completa.
Ejemplo: un Screen popup con un DataGridView de 3 columnas.
WaRPGate: Select Active Profile (@Profile)
Disponible en la paleta de comandos y en el menú contextual del editor sobre un archivo .warp. Abre un selector con los perfiles (@Profile) declarados en el .warpcfg del proyecto activo, más la opción “Use project default”. La elección queda recordada para ese proyecto (persiste al reiniciar VSCode) y se refleja en una barra de estado propia (ícono de capas), que también abre el mismo selector con un clic.
Sin una elección explícita, la extensión no envía --profile al compilador y el CLI cae solo al Default Profile declarado en @Project — ver Archivo de proyecto.
WaRPGate: Select Layout Block
Cuando un Program (reporte) tiene más de un Block en su @Layout, este comando elige cuál mostrar en el Preview Layout — el preview siempre muestra un único Block a la vez.