Combinadores de Lookahead, Control de Flujo y Etiquetado
Estos combinadores realizan aserciones de ancho cero (Peek, Not), bifurcación condicional (If), señalización de control de flujo (Skip), etiquetado de posiciones de tokens (Tag), fusión de tokens (Tokenize) y captura diagnóstica de errores (ErrorCombinator).
1. Peek (Lookahead Positivo)
Evalúa combinator dentro de un analyzer.failure_scope() aislado y siempre restaura el checkpoint en un bloque finally, de modo que el cursor nunca avanza.
Constructor
Peek(combinator: Combinator)
Forma de Retorno y Diagnósticos
- El hijo coincide (
res is not None): Restaura el checkpoint y retorna[](para que los combinadoresSeqenvolventes continúen con éxito sin añadir un nodo al árbol). - El hijo falla (
res is None): Restaura el checkpoint, reportascope.candidate(oPositiveLookaheadFailedsi no se preparó un candidato) y retornaNone.
Ejemplo
from gram.core.combinators import MatchToken, Peek
from gram.core.lexer import Token
assert_next_is_ident = Peek(MatchToken(Token.IDENT))
2. Not (Lookahead Negativo)
Afirma que combinator no coincide en parser.pos. Evalúa combinator dentro de un analyzer.failure_scope() aislado y siempre restaura el checkpoint.
Constructor
Not(combinator: Combinator)
Forma de Retorno y Diagnósticos
- El hijo falla (
res is None): Descarta el fallo interno del hijo, restaura el checkpoint y retorna[]. - El hijo coincide (
res is not None): Restaura el checkpoint, reportaNegativeLookaheadFailed(GrammarError) y retornaNone.
Ejemplo
from gram.core.combinators import MatchKeyword, Not
not_return = Not(MatchKeyword("return"))
3. If (Bifurcación Condicional)
Evalúa una o más conditions especulativamente dentro de analyzer.failure_scope() y se bifurca hacia ok o fail.
Constructor
If(
conditions: Sequence[Combinator] | Combinator,
ok: Sequence[Combinator] | Combinator | None = None,
fail: Sequence[Combinator] | Combinator | None = None,
*,
restore: bool = False,
)
- Pasar una
listotupleparaokofaillo envuelve automáticamente enSeq(*branch). - Pasar una secuencia vacía en
conditionslanzaEmptyIfConditions(GrammarError) en tiempo de construcción.
Formas de Retorno
- Cuando todas las
conditionstienen éxito:restore=False(por defecto): Mantiene los tokens de la condición consumidos.- Si se especifica
ok: evalúaoky retorna[*cond_results, ok_res](o restaura el checkpoint inicial y retornaNonesiokfalla). - Si
ok is None: retornacond_results.
- Si se especifica
restore=True: Restaura el checkpoint inicial antes de ejecutarok.- Si se especifica
ok: retornaanalyzer.visitor(self.ok). - Si
ok is None: retornacond_results.
- Si se especifica
- Cuando cualquier condición falla: Restaura el checkpoint inicial.
- Si se especifica
fail: retornaanalyzer.visitor(self.fail). - Si
fail is None: reporta el fallo candidato del ámbito de la condición (oIfConditionNotMet) y retornaNone.
- Si se especifica
Ejemplo
from gram.core.combinators import If, MatchKeyword, MatchToken
from gram.core.lexer import Token
branch = If(
conditions=MatchKeyword("let"),
ok=MatchToken(Token.IDENT),
fail=MatchToken(Token.NUMBER),
)
4. Skip y Señales de Control de Flujo
Skip(signal) lanza signal() cuando es visitado. Las señales heredan de ControlFlowSignal (gram.core.combinators.signals).
Constructor
Skip(signal: type[ControlFlowSignal])
Referencia de Señales (gram.core.combinators.signals)
| Clase de Señal | Manejada Por | Efecto |
|---|---|---|
SkipFlowSignal |
Seq, Many, Some, Separator |
En Seq: detiene la secuencia anticipadamente y retorna los nodos recolectados hasta el momento. En Many/Some: si parser.pos == checkpoint.pos, restaura el checkpoint y sale del bucle; si se consumieron tokens en la iteración actual, los conserva y continúa a la siguiente iteración. En Separator: restaura el checkpoint del separador y sale del bucle. |
BreakFlowSignal |
Many, Some, Separator |
Se propaga a través de Seq. Restaura el checkpoint de la iteración/separador actual y sale limpiamente del bucle de repetición. |
BacktrackSignal |
Alt |
Se propaga a través de Seq. Descarta la rama actual de Alt, restaura el checkpoint de Alt e intenta con la siguiente alternativa. |
CutFlowSignal |
Alt (vía ASTState.failure_scope) |
Se propaga a través de Seq. Se compromete con la rama actual de Alt: arrastra el _failure_candidate de la rama, restaura el checkpoint de Alt y aborta Alt sin probar las ramas restantes. |
AbortFlowSignal |
Invocador / nivel superior | Se propaga sin ser capturada a través de Seq y Alt para abortar el parseo inmediatamente. |
Ejemplo
from gram.core.combinators import Alt, MatchKeyword, MatchToken, Seq, Skip
from gram.core.combinators.signals import BacktrackSignal, CutFlowSignal, SkipFlowSignal
from gram.core.lexer import Token
# Salida anticipada en Seq: retorna [LiteralNumber(1)] y se detiene en pos=1
early_seq = Seq(
MatchToken(Token.NUMBER),
Skip(SkipFlowSignal),
MatchToken(Token.NUMBER),
)
# Forzar a Alt a descartar la rama 1 e intentar la rama 2
alt_backtrack = Alt(
Seq(MatchKeyword("int"), Skip(BacktrackSignal)),
Seq(MatchKeyword("int"), MatchToken(Token.IDENT)),
)
5. Tag
Asocia, elimina o afirma etiquetas de cadena en el índice del token actual (parser.pos) dentro del diccionario seguro ante checkpoints parser.tk_tags: dict[int, frozenset[str]]. Nunca consume tokens (parser.pos no cambia).
Constructor y Métodos de Clase
Tag(tag: str, mode: Literal["add", "remove", "exists", "not_exists"] = "add")
Tag.add(tag: str) -> Tag
Tag.remove(tag: str) -> Tag
Tag.exists(tag: str) -> Tag
Tag.not_exists(tag: str) -> Tag
Valor de Retorno y Diagnósticos
- Éxito: Retorna el
TokenTypeenparser.possin avanzarparser.pos. - Fallo / EOF:
- Reporta
TagUnexpectedEOF(ParserError) si el flujo está vacío. - Reporta
RequiredTagMissing(GrammarError) simode == "exists"ytagno está presente enparser.pos. - Reporta
ForbiddenTagPresent(GrammarError) simode == "not_exists"ytagestá presente enparser.pos.
- Reporta
6. Tokenize
Evalúa una secuencia de combinadores secundarios productores de tokens (Seq(*combinators)) y fusiona todos los tokens coincidentes en un único TokenType cuyo .token es un CustomToken(name=..., value=combined_value).
Constructor
Tokenize(
*combinators: Combinator,
token_name: str = "CustomToken",
name: str | None = None,
join_char: str = "",
)
- Si se especifica
name, anula atoken_name. - Lanza
EmptyTokenizeCombinators(GrammarError) en tiempo de construcción si se invoca sin combinadores secundarios.
Valor de Retorno y Diagnósticos
- Cada resultado hijo debe ser un
TokenType, un objeto con atributo.token: TokenType(comoASTTokenNode), o una lista/tupla anidada de estos. Si algún hijo produce un objeto que no sea token (como unASTRef),Tokenizerestaura el checkpoint, reportaInvalidTokenizeElement(GrammarError) y retornaNone. - Coincidencia: Retorna un
TokenTypesintetizado en la línea y columna del primer token coincidente, con su.valueunido porjoin_char.
Ejemplo
from gram.core.combinators import MatchToken, Tokenize
from gram.core.lexer import Token
decorator_tok = Tokenize(
MatchToken(Token.AT),
MatchToken(Token.IDENT),
name="DECORATOR",
join_char="",
)
7. ErrorCombinator
Extrae y retorna instancias de Error registradas previamente desde la primera lista no vacía de errores entre analyzer._errors, parser._errors o StackError.__err__.
Constructor
ErrorCombinator(length: int = 1)
length=0: Extrae todos los errores registrados.length=N($N > 0$): Extrae hasta losNerrores más recientes.
Forma de Retorno y Diagnósticos
- Cuando existen errores: Elimina los errores seleccionados de la lista de errores activa y los retorna como
list[errors.Error]en orden cronológico inverso (el más reciente primero). - Cuando no existen errores: Reporta
NoErrorsToCatch(GrammarError) y retornaNone.
Nota: Para registrar errores en
_errorssin terminar el proceso antes de queErrorCombinatorse ejecute, establececonfig.ERROR_EXIT_ON_ERROR = Falseo registra un hook de error que retorneErrorAction.SUPPRESS_RAISE.