Gramática de lectura y correspondencia con el parser
Antes: sintaxis. Profundizar: arquitectura.
Esta gramática resume las producciones implementadas. No reemplaza al parser
Pratt ni promete aceptar todos los programas que una EBNF aislada produciría:
la visibilidad, los tipos y los lugares válidos de ref se validan después.
Notación
Comillas indican texto literal. [...] indica opcional; {...} repetición;
| separa alternativas. expr usa la tabla de precedencia de la referencia.
sep es fin de línea o ;; las listas permiten nuevas líneas y coma final.
programa = { sentencia [sep] } ;
sentencia = declaracion | funcion | clase | interfaz | enum | inicializador | select
| "return" [expr] | "throw" expr
| "break" | "continue" | ciclo | captura
| ("print" | "echo") ["("] expr [")"] | expr ;
variable = "$" identificador ;
declaracion = [visibilidad] ["static"] ["const"]
("let" [tipo] | tipo) variable ["=" expr]
{"," variable ["=" expr]} ;
funcion = [visibilidad] ["abstract"] ["static"] "func" identificador firma (bloque | [sep]) ;
firma = "(" [parametros] ")" [":" tipo] ;
parametros = parametro {"," parametro} [","] ;
parametro = [visibilidad] ["ref"] tipo variable ["=" expr] ;
closure = "func" firma bloque ;
clase = visibilidad ["abstract"] ["static"] "class" identificador
["extends" identificador]
["implements" identificador {"," identificador}] "{" {miembro} "}" ;
interfaz = visibilidad "interface" identificador
["extends" identificador {"," identificador}] "{" {protoMetodo} "}" ;
enum = visibilidad "enum" identificador [":" tipo] "{" {casoEnum} "}" ;
casoEnum = "case" identificador ["=" expr] [sep] ;
protoMetodo = [visibilidad] "func" identificador "(" [parametros] ")" [":" tipo] [sep] ;
miembro = declaracion | funcion | inicializador ;
inicializador = "Init" [identificador] "(" [parametros] ")" bloque ;
visibilidad = "public" | "private" | "protected" ;
tipo = simple {"|" simple} ["?"] ;
simple = nombreTipo ["<" tipo {"," tipo} ">"] ;
bloque = "{" {sentencia [sep]} "}" ;
ciclo = "while" "(" expr ")" bloque
| "do" bloque "while" "(" expr ")"
| "foreach" "(" expr "as" [variable "=>"] variable ")" bloque ;
captura = "try" bloque "catch" "(" variable ")" bloque ;
select = "select" "{" {casoSelect} "}" ;
casoSelect = ("case" (("send" "(" expr "," expr ")") | ("recv" "(" expr ")") | (variable "=" "recv" "(" expr ")")) | "default") ":" {sentencia [sep]} ;
array = "[" [elementoArray {"," elementoArray} [","]] "]" ;
elementoArray = expr | "..." expr ;
map = "{" [elementoMap {"," elementoMap} [","]] "}" ;
elementoMap = expr ":" expr | "..." expr ;
match = "match" "(" expr ")" "{" {brazo} "}" ;
brazo = ("default" | expr {"," expr}) "=>" (expr | bloque) [","] ;
llamada = expr "(" [argumento {"," argumento} [","]] ")" ;
argumento = expr | "ref" variable | identificador ":" expr | "..." expr ;
yield = "yield" [expr ["=>" expr]] ;
comprobacion = expr ("is" | "instanceof") tipo ;
acceso = expr ("->" | "?->" | "::") nombreMiembro ;
indice = expr "[" [expr] "]" ;
nuevo = "new" identificador "(" [argumentos] ")" ;
asincrono = "async" bloque ;
Restricciones que la notación no expresa
protectedsólo corresponde a miembros, no a clases/funciones globales.staticrequiere visibilidad explícita.Inity closures no la llevan.- El parser conserva parámetros sin tipo para poder emitir
JOSS-TYPE-011; la gramática de código válido exige el tipo. constexige inicializador y tiene su propia ruta de parseo.- Los nombres de tipos canónicos se describen en tipos; aceptar el nombre en el parser no prueba que exista una clase.
- La única genericidad semántica implementada es la de arrays/maps.
- Un índice vacío sólo sirve para append como destino de asignación.
- En expresión,
{}se interpreta como mapa vacío. Un cuerpo obligatorio se parsea como bloque.{ "a": 1 }se distingue de un bloque por:. - El ternario usa
cond ? expr : expr,cond ?: expro condicional de una sola rama(cond) ? bloque; sus ramas admiten bloques o expresiones.matchevalúa y ejecuta tanto expresiones como bloques de sentencias multilínea. - Las cadenas con comillas dobles admiten interpolación de expresiones
${expr}desazucarada a concatenación.. Se incorporan los operadores de rango numérico.., decremento postfix--y asignación nula coalescente??=. async expresióntodavía es una ruta del parser, con evaluación anticipada del argumento;async(...)se rechaza. La forma recomendada es el bloque.- Llamadas, arrays y firmas permiten comas finales. La continuidad de una
expresión tras un salto se decide por
isExpressionContinuation; no todos los tokens postfix tienen idéntico tratamiento (por ejemplo?->).
Del texto al nodo
| Producción | Parser | Nodo AST |
|---|---|---|
| Programa | ParseProgram |
Program |
| Expresión por precedencia | parseExpression, registros prefix/infix |
Expression y tipos concretos |
| Asignación | parseAssignExpression |
AssignExpression |
| Variable declarada | Rutas de parseStatement |
LetStatement, MultiLetStatement |
| Función global/método | parseMethodStatement |
MethodStatement |
| Closure | parseFunctionLiteral |
FunctionLiteral |
| Tipo | parseTypeReference |
Token normalizado conservado en declaración/firma |
| Clase / Init | parseClassStatement, parseInitStatement |
ClassStatement, InitStatement |
| Interfaz | parseInterfaceStatement |
InterfaceStatement |
| Enum | parseEnumStatement |
EnumStatement |
| Select | parseSelectStatement |
SelectStatement |
| Yield | parseYieldExpression |
YieldExpression |
| Spread | parseSpreadExpression |
SpreadExpression |
| Comprobación de tipo | parseIsInfix |
IsExpression |
| Ciclos | parseForeachStatement, parseWhileStatement, parseDoWhileStatement |
Sus nodos de sentencia |
| Error | parseTryCatchStatement, parseThrowStatement |
TryCatchStatement, ThrowStatement |
| Defer | parseDeferStatement |
DeferStatement |
| Async | parseAsyncExpression |
CallExpression a async con función capturada |
Los archivos son pkg/parser/parser*.go, ast*.go, lexer.go y token.go.
Para añadir sintaxis, actualiza pruebas positivas y negativas antes de cambiar
esta referencia. No conviertas nombres de tokens residuales en features públicas.