Combinadores Estructurales
Los combinadores estructurales componen combinadores secundarios (hijos) en secuencias ordenadas, alternativas priorizadas, elementos opcionales y bloques delimitados.
1. Seq
Seq evalúa los combinadores secundarios en orden lineal. También puedes componer secuencias usando el operador + (comb1 + comb2).
Constructor
Seq(*combinators: Combinator)
- Lanza
EmptySequence(GrammarError) en tiempo de construcción si se invoca sin argumentos.
Ejecución y Forma de Retorno
- Guarda un punto de restauración (checkpoint
"SEQ-Analysis") antes de evaluar a los hijos. - Para cada combinador secundario:
- Si el hijo retorna
None,Seqrestaura el checkpoint, prepara/emiteSequenceElementMismatch(a menos que ya se hubiera preparado un fallo más profundo) y retornaNone. - Si el hijo retorna
[](por ejemplo, unOptomitido, unManycon 0 coincidencias,PeekoNot),Seqtrata el paso como exitoso y omite[]de la lista de salida. - En cualquier otro caso,
Seqagrega el resultado del hijo aresults.
- Si el hijo retorna
- Señales de Control de Flujo:
SkipFlowSignales capturada porSeq: la secuencia se detiene de inmediato y retorna losresultsacumulados hasta el momento como una coincidencia exitosa.BacktrackSignal,BreakFlowSignal,CutFlowSignalyAbortFlowSignalno son capturadas porSeqy se propagan hacia el combinador contenedor (Alt,Many,Some,Separator).
- Valor de Retorno:
list[Any]con los resultados de los hijos distintos de[]en caso de éxito, oNoneen caso de fallo.
Ejemplo
from gram.core.combinators import MatchKeyword, MatchToken, Opt, Seq
from gram.core.lexer import Token
# Usando Seq(...) o el operador +:
let_binding = Seq(
MatchKeyword("let"),
MatchToken(Token.IDENT),
Opt(MatchToken(Token.ASSIGN) + MatchToken(Token.NUMBER)),
)
2. Alt (Alternativa)
Evalúa las ramas candidatas en orden hasta que la primera rama devuelva un resultado distinto de None. También puedes componer alternativas usando el operador | (comb1 | comb2).
Constructor
Alt(*combinators: Combinator, ignore: bool = False)
ignore: Si esTrue, elVisitorno incrementa la profundidad de anidamiento ni registra este contenedorAlten elRayTracing(ASTAnalyzer.is_valid_entrypointestableceignore = Trueen elAltraíz del punto de entrada).- Lanza
EmptyAlternatives(GrammarError) en tiempo de construcción si se invoca sin ramas.
Ejecución, Señales y Diagnósticos
- Cada rama se evalúa dentro de un
analyzer.failure_scope()aislado. - Si una rama retorna
None(o lanzaBacktrackSignal),Altrestaura el checkpoint, conserva el candidato de fallo más profundo (best_staged) entre las ramas rechazadas e intenta con la siguiente rama. - Si una rama lanza
CutFlowSignal:Altse compromete con el fallo candidato de esa rama (descartando los candidatos de ramas anteriores), restaura el checkpoint, deja de intentar con las ramas restantes y reporta el error.
- Si todas las ramas fallan,
Altreportabest_staged(oNoAlternativeMatcheden la posición inicial si ninguna rama preparó un error específico) y retornaNone. - Valor de Retorno: El valor de retorno exacto de la primera rama coincidente, o
None.
Ejemplo
from gram.core.combinators import Alt, MatchToken
from gram.core.lexer import Token
atom = (
MatchToken(Token.NUMBER)
| MatchToken(Token.STRING)
| MatchToken(Token.IDENT)
)
3. Opt (Opcional)
Evalúa un único combinador secundario con cardinalidad $0..1$ dentro de analyzer.failure_scope().
Constructor
Opt(combinator: Combinator)
- Lanza
InvalidOptionalTarget(GrammarError) en tiempo de construcción sicombinatorno es una instancia deCombinator.
Ejecución y Fallos Parciales Profundos
- Coincidencia: Retorna el resultado del combinador secundario directamente.
- Omisión Limpia (0 tokens consumidos): Si el hijo falla sin avanzar más allá de
checkpoint.pos(candidateesNoneocandidate[0] <= checkpoint.pos),Optrestaura el checkpoint sin reportar un error y retorna[]. - Fallo Parcial Profundo (
candidate[0] > checkpoint.pos): Si el hijo consumió uno o más tokens antes de fallar dentro de una subregla de múltiples pasos (por ejemplo, coincidiendo con=enOpt(Seq(MatchToken(Token.ASSIGN), MatchToken(Token.NUMBER)))y fallando por la falta del número),Optrestaura el checkpoint, reporta el candidato de fallo profundo y retornaNone.
Ejemplo
from gram.core.combinators import MatchToken, Opt, Seq
from gram.core.lexer import Token
optional_initializer = Opt(
Seq(MatchToken(Token.ASSIGN), MatchToken(Token.NUMBER))
)
4. Enclosed
Coincide con un Token de apertura, un combinador de contenido interno (content) y un Token de cierre.
Constructor
Enclosed(
open: Token,
content: Combinator,
close: Token,
skip_whitespace: bool = True,
allow_empty: bool = True,
)
openyclosedeben ser miembros del enumToken; cualquier otro tipo lanzaInvalidDelimiterType(GrammarError) en tiempo de construcción.skip_whitespace: Si esTrue, invocaparser.enter_delimiter()/parser.exit_delimiter()yparser.skip_whitespace(), lo que incrementaparser.bracket_depthy omiteToken.NEWLINE,Token.INDENTyToken.DEDENTdentro de los delimitadores.allow_empty: Si esTrue, yclosesigue inmediatamente aopen,contentno se evalúa yEnclosedretorna[]. Si esFalse, delimitadores vacíos (o una coincidencia decontentque consuma 0 tokens) fallan conEmptyEnclosedContent.
Valor de Retorno y Diagnósticos
- Coincidencia: Retorna el resultado de
content(o[]cuando está vacío yallow_empty=True). Los tokensopenyclosese consumen pero no se incluyen en el valor retornado. - Fallo: Restaura el checkpoint inicial, reporta uno de
UnexpectedEOFBeforeOpenDelimiter,OpeningDelimiterMismatch,EmptyEnclosedContent,EnclosedContentMismatch,UnclosedDelimiterEOFoClosingDelimiterMismatch, y retornaNone.
Ejemplo
from gram.core.combinators import Enclosed, Separator
from gram.core.lexer import Token
param_list = Enclosed(
open=Token.LPAREN,
content=Separator(values=[Token.IDENT], sep=Token.COMMA),
close=Token.RPAREN,
skip_whitespace=True,
allow_empty=True,
)