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

WARP & Warpgate: la evolución en el desarrollo para IBM i

Read this in English

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:

  1. Abrir VSCode → pestaña Extensiones (Ctrl+Shift+X).
  2. 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 con Member/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 en Output — puramente local, nunca se conecta al IBM i (aunque el CL generado ya referencia rutas remotas, según @Connection/@Deployment).
  • build: hace lo mismo que generate, 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 .warp de 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

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ínea Nombre tipo(...) por columna. Sólo char/number/date — ver Tipos de dato. Modificadores disponibles: Description(...), Default(...), AllowNull.
  • @Indexes: PrimaryKey (obligatoria, sus campos no pueden ser AllowNull), Unique, Index — cada uno como Kind(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 devuelve CurrentInput() cuando el cursor cae ahí. habilitado es opcional (default true): literal, &Variable o función de retorno number.
  • 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

EventoCuándo dispara
InitUna sola vez, antes del primer despliegue.
LoadUna sola vez, antes del primer despliegue — típicamente llena el DataGridView con LoadRow().
EnterAl presionar Enter (sin tecla de función activa).
RefreshAl presionar F5, si está declarada en @FunctionKeys del proyecto.
CloseFijo 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 entre Input/DataGridView de la misma pantalla.
  • Sólo un DataGridView por Screen.
  • Sólo válido dentro de @Layout de un Screen, nunca de un Program.

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 varchar en @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 ADDPFM si 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 cualquier Export(...) 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 de If/While/For/Event/etc.).
  • En un Screen: sólo a nivel superior de Event Init (no anidado, y no en ningún otro Event) — Init corre una única vez, antes del loop principal de la pantalla, el mismo lugar donde iría el prólogo de un Program:
@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 cualquier For Each/New sobre Tabla en 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 Exit se pueden sobreescribir por objeto en el @Properties de un .warp puntual.
  • @Screen: tamaño de pantalla por defecto (24x80 si se omite) y las teclas de función (@FunctionKeys) disponibles para los Screen del proyecto — la etiqueta que se muestra en la línea de atención. Un Screen liga una tecla a un evento propio con Event 'Nombre' <n> ... EndEvent.
  • @Profile "Nombre": un ambiente de despliegue (Dev, UAT, Prod, el nombre es libre). Cada perfil agrupa su propia @Connection, @Deployment y, opcionalmente, @License. Un .warpcfg debe declarar al menos un @Profile.
    • @Connection: cómo llega el compilador al IBM i para --action build/reverse (no aplica a validate/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). El Host/Port/Fingerprint los 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.

ClaveDescripción
HostHost del servicio de licencias. Si está presente, el modo es organización y File se ignora.
PortPuerto TCP del servicio, de 1 a 65535. Por defecto 7443.
FingerprintHuella SHA-256 del certificado del servicio: 64 caracteres hexadecimales (se aceptan mayúsculas y separadores :).
SPNNombre de servicio Kerberos. Por defecto warpgate-license/<host>.
FileRuta del archivo de licencia individual.

Sin Host, el modo es individual.

Flags y variables de entorno

FlagVariable de entornoEquivale a
--license-hostWARPGATE_LICENSE_HOSTHost
--license-portWARPGATE_LICENSE_PORTPort
--license-fingerprintWARPGATE_LICENSE_FINGERPRINTFingerprint
--license-spnWARPGATE_LICENSE_SPNSPN
--license-fileWARPGATE_LICENSE_FILEFile

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ódigoSignificado
3No hay licencia disponible. El compilador muestra un mensaje con lo que se debe hacer.
4Se perdió la licencia a mitad de la ejecución.
130Ejecució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.

  1. 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
    
  2. Enviar a warpgate@softwarehouse.pe:
    • El archivo request.json generado.
    • El identificador del equipo que el comando imprime en pantalla (necesario para emitir la licencia).
  3. Software House responde con un archivo license.json firmado, ligado a ese equipo, válido por 90 días.
  4. 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
  5. 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ódigoSituación
E00284Port de @License es 0.
E00285Host de @License no es un host válido.
E00286Fingerprint no es una huella SHA-256 válida.
E00287Aviso: hay Host sin Fingerprint.
E00288Aviso: 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

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)

  1. 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
    
  2. Instalar la licencia y generar el certificado, como ese usuario:
    sudo -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>
    
    Anotar la huella SHA-256 que imprime el segundo comando: ese valor es el Fingerprint.
  3. 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ónProgramScreenTable
@Documentationnivel superiornivel superioranidada en @Structure
@Propertiessísísí (sólo Type)
@Structurenonosí
@Fieldsnonoanidada en @Structure
@Indexesnonoanidada en @Structure
@Referencessísíno
@Variablessísíno
@Parameterssínono
@Layoutsí (opcional)síno
@Sourcesí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 un For Each/New.
  • Cada sección de nivel superior lleva @; los nombres de control dentro de @Layout pueden llevar @ opcionalmente (@Label/Label son equivalentes).

Índice de la referencia

@Documentation

Metadatos informativos, no afectan la generación de código.

En Program/Screen (nivel superior)

Pares planos Clave : valor:

ClaveDescripción
Author NameNombre de quien escribió el programa.
Author UserUser ID de quien lo escribió.
App IdIdentificador corto de la aplicación.
App NameNombre visible de la aplicación.
App DescriptionDescripción larga de la aplicación.
Module IdIdentificador corto del módulo.
Module NameNombre visible del módulo.
Module DescriptionDescripción larga del módulo.
Created AtFecha/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 @Creation y cualquier cantidad de @Change.
    • @Creation: Date, Author, User, Company, Reason.
    • @Change (repetir el bloque por cada entrada): Date, Type (por convención breaking/feature/fix/security/performance/refactor/config/docs, no forzado por el compilador), Impact (por convención high/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

ClaveDescripción
TypeEl tipo de objeto que compila este archivo (Program).
NameNombre del programa.
DescriptionDescripció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 ModeModo de impresión, para un programa tipo reporte.
CommitmentSi 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 ExitSi 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.
DDSNombre del DDS al que está atado este programa.
PathCarpeta 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:

ClaveDescripción
PopupGenera 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 RowFila (1-based) de la esquina superior izquierda de la ventana. Requerida si Popup: True.
Window ColumnColumna (1-based) de la esquina superior izquierda de la ventana. Requerida si Popup: True.
Window RowsAlto de la ventana, en filas. Requerida si Popup: True.
Window ColumnsAncho 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

ClaveDescripción
NameNombre de la tabla. Debe empezar con letra, sólo letras/dígitos, máximo 10 caracteres (límite de nombre de objeto DDS).
DescriptionDescripció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:

ModificadorDescripció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.
AllowNullPermite 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] )
}
KindDescripción
PrimaryKeyAcceso primario de la tabla. Sólo uno por tabla; sus campos no pueden ser AllowNull.
UniqueAcceso secundario único.
IndexAcceso 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.

ClaveDescripción
ProgramsOtros Program .warp que este referencia (para Call(Nombre, ...) resuelto en tiempo de compilación).
ScreensScreen .warp que este referencia — igual que Programs, habilita invocarlo con Call(Nombre, ...) (típicamente un Screen con @Properties/Popup: True, mostrado como ventana emergente).
TablesTablas .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)

TipoLímite en @FieldsLímite en @Variables
char1–32.7661–16.773.104
varcharno válido en @Fields1–16.773.100
number (precisión)1–301–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

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)sólo @VariablesHereda el tipo de un campo NombreCampo de @Structure de alguna tabla referenciada.
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.

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é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 (la cuenta de elementos vuelve a 0).
Remove(indice)sentenciaQuita el elemento en la posición indice (1-based, tipo number) y recorre los siguientes una posición hacia atrás.
Count()expresiónDevuelve 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 otro struct, ni usar list/matrix como miembro.

  • 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 anidado dentro de otro struct, ni struct como parámetro de un programa externo (@Parameters/Call) — sólo como parámetro de una Procedure/Function interna.

@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á.

ModoDescripción
inParámetro de entrada: quien llama pasa un valor, este programa no puede devolverlo modificado.
inoutEntrada/salida: quien llama pasa un valor, y este programa puede modificarlo de vuelta.
outSalida: 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

ControlSintaxisVálido enDescripción
LabelLabel(x, y, texto)Program, ScreenEtiqueta 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í.
BlockBlock("Nombre")[ Label(...), ... ]ProgramGrupo nombrado e imprimible de Labels — se imprime una instancia con Print("Nombre") desde @Source.
InputInput("Nombre", x, y, texto, &Variable[, habilitado])ScreenCampo 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.
ColumnColumn(texto, variableOCampo[, Input|Hidden])sólo dentro de DataGridViewEl 3er argumento opcional hace la columna editable (Input) o invisible (Hidden).
DataGridViewDataGridView("Nombre", x, y, filas[, separador])[ Column(...), ... ]ScreenGrilla 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(&param)
    ...
EndProc

Function Nombre(&param) tipoRetorno
    ...
    Return expr
EndFunc
  • Procedure (sin retorno, sub es sinónimo): se invoca con Do 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

SentenciaUso
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

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 a Tabla para todo el programa (ADDPFM + OVRDBF, antes de cualquier open). En Program, sólo a nivel superior de @Source; en Screen, sólo a nivel superior de Event Init (ver @Source: Event y Screen). A lo sumo una vez por tabla.
  • Export(Tabla, destino): copia el miembro activo de Tabla a un stream file del IFS (CPYTOIMPF, delimitado por coma). Sólo válido en Program (no en Screen); 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

EventoCuándo dispara
EnterAl presionar Enter (sin tecla de función activa). CurrentInput() identifica qué Input tenía foco, decodificado de la posición del cursor tras EXFMT.
CloseFijo a F3 en el DSPF generado (CF03) — siempre corre (incluso vacío) y termina la pantalla; no requiere declaración para atar la tecla.
LoadUna 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.
RefreshAl presionar F5, si F5 está declarada en @FunctionKeys del .warpcfg y no está tomada por un Event 'Nombre' 5 personalizado.
InitUna 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.

SentenciaUso
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 ... EndForRecorre 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ónRetornoDescripció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()dateFecha 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ónParsea 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 argumentoQuita blancos a la izquierda. Un solo argumento.
RTrim(&Variable)igual al argumentoQuita blancos a la derecha. Un solo argumento.
Length(&Variable)numberTamañ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)numberBusca 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ónRetornoDescripció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)numberDiferencia 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)numberIgual que DateDiff, sobre time, con Hours/Minutes/Seconds.
TimestampDiff(&Ts1, &Ts2, Unidad)numberIgual que DateDiff, sobre timestamp, con las 6 unidades habilitadas: Seconds/Minutes/Hours/Days/Months/Years.
DateAdd(&Fecha, cantidad, Unidad)dateSuma (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)timeIgual que DateAdd, sobre time, con Hours/Minutes/Seconds.
TimestampAdd(&Ts, cantidad, Unidad)timestampIgual 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])dateConvierte char/varchar a date.
NumberToDate(number[, format])dateConvierte number a date — mismas reglas de format que StringToDate.
StringToTime(string[, format])timeConvierte char/varchar a time.
StringToTimestamp(string[, format])timestampConvierte 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()timestampTimestamp 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

OperadorUso
+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 un For Each/New sobre 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/Remove como 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

ClaveDescripción
NameNombre del proyecto.
DescriptionDescripción del proyecto.
VersionVersión del proyecto (string libre, p.ej. 1.0).
Default ProfileNombre 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)

ClaveDescripción
NameNombre del generador.
DescriptionDescripción del generador.
LanguageLenguaje de destino. Valores: RPGLE, RPG.

@Paths (anidada en @Generator)

ClaveDescripción
ConfigRuta al .warpcfg de este proyecto.
OutputCarpeta donde se escribe el DDS/RPG generado.

.warpcfg — @GeneratorConfig

ClaveDescripciónValores
Date FormatFormato 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 SeparatorCarácter separador de fecha (p.ej. / en date(*dmy/)). Un solo carácter.—
Time FormatFormato 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 SeparatorCará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 FormatDefault 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 ModeQué 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
CommitmentHabilita 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 ExitSi 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 NamePatró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):

  1. El flag --profile <Nombre> del CLI, sin distinguir mayúsculas.
  2. Si se omite, Default Profile de @Project (sección .warproj — @Project más arriba).
  3. Si tampoco hay Default Profile pero el .warpcfg sólo declara un @Profile, se usa ese.
  4. 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)

ClaveDescripción
RowsCantidad de filas de pantalla. Numérico, default 24.
ColumnsCantidad 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:

ClaveDescripciónValores
HostHostname o IP del IBM i. Requerida.—
UserPerfil de usuario para conectar. Requerida.—
Auth MethodCó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 FileRuta al archivo de clave privada. Requerida sólo si Auth Method: key.—
ProtocolProtocolo de transporte. Requerida. ftp no puede combinarse con Auth Method: key.ssh, sftp, ftp
PortPuerto TCP. Numérico, default 22.—
TimeoutTimeout 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.

  1. Generar el par de llaves (en la máquina donde corre el compilador/VSCode, no en el IBM i):
    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_ibmi -N ""
    
    Esto crea id_ed25519_ibmi (privada — la ruta que va en Key File) y id_ed25519_ibmi.pub (pública). -N "" deja la llave sin passphrase, requisito para Auth Method: key.
  2. Copiar el contenido de id_ed25519_ibmi.pub (una sola línea) al final del archivo .ssh/authorized_keys dentro 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.
  3. Verificar que Key File en @Connection apunte a la ruta de la llave privada en la máquina local (no la .pub), y que Auth Method sea key.

@Deployment (anidada en @Profile)

Dónde/cómo desplegar y compilar en el IBM i:

ClaveDescripciónValores
CL SourceNombre del member de fuente CL generado (nombre de objeto OS/400, máx. 10 caracteres). Requerida.—
CleanupSi se elimina el fuente staged tras compilar exitosamente.true, false
Target ReleaseRelease de OS/400 destino de la compilación (p.ej. V7R4M0).—
OptimizationNivel de optimización del compilador.—
DebugSi se compila con la vista de debug habilitada.true, false
Temp PathDirectorio staging del IFS usado durante el deploy. Requerida.—
Data LibraryBiblioteca donde se crean los objetos físico/lógico de DDS compilados (nombre de objeto OS/400). Requerida.—
Objects LibraryBiblioteca 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 TablesMember de fuente que contiene el fuente DDS de físicos/lógicos (nombre de objeto OS/400). Requerida.—
Source ProgramsMember de fuente que contiene el RPGLE/CL generado (nombre de objeto OS/400).—
Compile LibraryLista 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.

ClaveDescripciónValores
HostHost del servicio de licencias. Con Host el modo es organización y File se ignora; sin Host, el modo es individual.—
PortPuerto TCP del servicio. Por defecto 7443.1-65535
FingerprintHuella SHA-256 del certificado del servicio (acepta : y mayúsculas).64 hex
SPNNombre de servicio Kerberos. Por defecto warpgate-license/<host>.—
FileArchivo 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

NivelSignificado
errorImpide la generación de código; el objeto no compila.
warningNo 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ódigoSituación
E00001Se esperaba una sección @Nombre y se encontró otra cosa.
E00128Un 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.
E00152Una variable de @Variables fue declarada pero nunca usada (warning).
E00195Uso inválido de RefreshSelectRow()/For Each Row fuera de un Screen con DataGridView.
E00221char fuera del rango permitido para una variable standalone en @Variables (1-16.773.104).
E00222number con precisión fuera de rango (1-63) en @Variables.
E00223number cuya escala es mayor que su precisión.
E00224varchar fuera del rango permitido (1-16.773.100).
E00238struct(Nombre) referencia un template que no existe en @Variables.
E00240Acceso &Variable.Miembro a un miembro que no existe en el template del struct.
E00242Se pasó una variable struct(OtroTemplate) donde se esperaba un template distinto (la comparación es por nombre exacto, no por estructura).
E00269La Description de una tabla (@Structure) supera los 50 caracteres que admite TEXT en DDS.
E00270La Description de un campo (@Fields) supera los 50 caracteres que admite TEXT en DDS.
E00271La Description de un índice (@Indexes) supera los 50 caracteres que admite TEXT en DDS.
E00281La 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.
E00284Port de @License es 0.
E00285Host de @License no es un host válido.
E00286Fingerprint de @License no es una huella SHA-256 válida.
E00287Aviso: @License tiene Host pero no Fingerprint.
E00288Aviso: @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.
E00292El perfil pedido con --profile o Default Profile no existe entre los @Profile declarados (se listan los disponibles).
E00293Hay 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 .warp en VSCode (warpgate.validateOnSave/validateOnOpen), si hay un .warproj resuelto para ese archivo.
  • Manualmente con warpgate --action validate — no toca IBM i, es puramente local.
  • Nota de licencia: validate no exige licencia; generate/build/reverse sí (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/Function como 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 @Layout como una pantalla de terminal IBM i 5250 (24x80 por defecto, o el tamaño configurado en @Screen del .warpcfg del 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.