GRAMÁTICA v2 — Motor de expresiones
La definición exacta de lo que se puede escribir en una expresión. Admite dos formas de escribir lo mismo —al estilo C# y al estilo Python—, y una expresión escrita con la versión anterior sigue valiendo tal cual. Si buscas ejemplos y no la definición, empieza por la guía rápida o el manual.
Principios
- Nivel 1 (Expresión): todo lo de este documento salvo la sección «Nivel 2».
- Identificadores, palabras clave y nombres de función case-insensitive
- Texto almacenado siempre en cultura invariante: punto decimal, fechas ISO.
- Comentarios:
// líneay/* bloque */(útiles en scripts; válidos también en expresiones).
Tokens
| Token | Forma | Ejemplo |
|---|---|---|
| Número | dígitos con . decimal opcional (sin separador miles) | 123, 0.5, 1234.56 |
| String | "..." o '...'; escapes \\ \" \' \n \t | "IVA", '21%' |
| Fecha | #yyyy-MM-dd#, #yyyy-MM-dd HH:mm#, #... HH:mm:ss# | #2026-06-10# |
| Booleano | true, false | |
| Null | null o None (alias, v2) | |
| Campo | [Nombre Campo] (admite espacios y acentos) | [Total Línea] |
| Identificador | letra o _ + letras/dígitos/_ | PageNumber, tipoIva |
| Agregado | AGG(identificador) — forma especial, compat reports | AGG(totalVentas) |
Gramática (EBNF) — Nivel 1
expresion := condicional
condicional := coalesce ( '?' expresion ':' expresion
| 'if' coalesce 'else' condicional )? // forma Python (v2)
coalesce := logicaOr ('??' logicaOr)*
logicaOr := logicaAnd (('||' | 'or') logicaAnd)*
logicaAnd := igualdad (('&&' | 'and') igualdad)*
igualdad := relacional (('==' | '!=' | '<>') relacional)*
relacional := aditiva (('<' | '<=' | '>' | '>=') aditiva)*
aditiva := multiplicativa (('+' | '-') multiplicativa)*
multiplicativa := unaria (('*' | '/' | '%') unaria)*
unaria := ('-' | '!' | 'not') unaria | postfija
postfija := primaria ('.' identificador argumentos?)* // solo los miembros permitidos
primaria := numero | cadena | fecha | 'true' | 'false' | 'null'
| '[' nombreCampo ']'
| 'AGG' '(' identificador ')'
| identificador argumentos? // símbolo o función whitelist
| '(' expresion ')'
argumentos := '(' (expresion (',' expresion)*)? ')'
and/or/notson alias amables de&&/||/!;<>es alias de!=;Nonedenull.- Forma condicional Python (v2):
valor if condición else otro≡condición ? valor : otro. =no es comparación ni asignación en expresiones. Error de parser con mensaje- Un identificador suelto se resuelve subiendo por la cadena de scopes del entorno
Tipos y conversiones
Tipos del lenguaje: Null, Boolean, Decimal, String, DateTime.
- Todo número es
decimal. Sindouble, sinintvisibles. +con dos números suma; con al menos un string concatena (el otro operando se- Resto de aritmética solo entre números; tipo incompatible = error de evaluación.
- Comparación entre números: numérica. Entre strings: ordinal case-sensitive
- División o módulo por cero = error de evaluación (igual que ambas bases de datos).
- Conversiones siempre explícitas vía funciones (
ToDecimal,ToString,ToDate).
Semántica de null (lógica trivaluada, compatible MS SQL / PostgreSQL)
| Operación | Resultado | ||
|---|---|---|---|
null + 5, null * x, -null | null (toda aritmética propaga) | ||
"a" + null | null (como +/`\ | \ | en SQL; Concat` ignora null) |
a == b con un null en runtime | null (desconocido) | ||
x == null / x != null (literal) | true/false — actúa como IS [NOT] NULL | ||
true or null | true | ||
false or null | null | ||
true and null | null | ||
false and null | false | ||
not null | null | ||
Condición de ?: (o if) = null | se toma la rama de false (como WHERE en SQL) | ||
null ?? x | x (equivale a Coalesce(null, x)) |
Funciones whitelist v1
Nombres en inglés estilo SQL/C# (los que el usuario de SQL ya conoce); case-insensitive. Alias en español: posibles más adelante, no en v1.
- Null:
Coalesce(a, b, ...)·IsNull(a, b)(alias deCoalescea 2) ·IIf(cond, siTrue, siFalse) - Matemáticas:
Abs·Round(x[, decimales])(away-from-zero, como SQL) ·Floor·Ceiling·Min(a, b)·Max(a, b) - Strings:
Upper·Lower·Trim·Len·Substring(s, inicio[, longitud])(inicio en 1, como SQL) ·Replace·Contains·StartsWith·EndsWith·PadLeft(s, ancho[, char])·PadRight·Concat(a, b, ...)(ignora nulls) - Fechas:
Today()·Now()(fijadas al crear el entorno raíz: constantes en todo el report) ·Year·Month·Day·AddDays·AddMonths·AddYears·DateDiffDays(a, b) - Conversión:
ToDecimal(x)·ToString(x)·ToDate(x)(parse invariante/ISO; fallo = error) - Formato:
Format(valor, "formato")— formatos .NET ("N2","dd/MM/yyyy"...). Única
Añadir una función = una entrada en el registro de funciones (nombre, aridad, tipos, pureza). Las funciones de nivel 1 son puras (sin efectos); Now/Today lo son por el reloj fijado.
Errores
Cuatro categorías, todas con posición (índice y extracto) y mensaje en español:
- Parse (
ExpressionParseException): token inesperado/esperado. Incluye el caso=→ «use ==». - Binding (
ExpressionBindException): símbolo o función desconocidos; sugerencia por - Permisos (
ExpressionPermissionException): símbolo de un área no accesible para el usuario. - Runtime (
ExpressionRuntimeException): división por cero, conversión fallida, tipo
Nivel 2 — Script
Reutiliza íntegra la gramática de expresión. Solo añade sentencias:
script := sentencia+
sentencia := declaracion | asignacion | si | mientras | porCada | devolver
| bloque | expresion ';'
declaracion := 'var' identificador '=' expresion ';'
asignacion := identificador '=' expresion ';'
si := 'if' '(' expresion ')' sentencia ('else' sentencia)?
mientras := 'while' '(' expresion ')' sentencia
porCada := 'foreach' '(' 'var' identificador 'in' expresion ')' sentencia
devolver := 'return' expresion? ';'
bloque := '{' sentencia* '}' // abre un scope hijo
Reglas (definitivas en v1):
- Las palabras clave de sentencia (
var,if,else,while,foreach,in,return) - En scripts
=es asignación (en expresiones sigue siendo el error pedagógico «use ==»). - Cada
bloqueabre un scope hijo;vardeclara en el scope actual (sombrea al exterior; - El script devuelve el valor del primer
returnalcanzado; sinreturn(oreturn;) → null. foreachitera listas (valoresIEnumerabledel entorno, opacos para el resto del- Presupuesto de pasos (
CompileOptions.MaxSteps, 1.000.000 por defecto) descontado en - Nivel del entorno: ejecutar un script contra un entorno
LanguageLevel.Expression - API del ERP: si el entorno expone las capacidades, los símbolos
QueryyCommand - Solo backend interpretado para scripts; el backend compilado es de expresiones.