Combinador de Validación Fluida (`Req`)
Req (gram.core.combinators.req.Req) valida que el token en parser.pos coincida con un tipo de token objetivo y ejecuta una cadena de funciones de validación y transformación sobre una copia superficial (copy.copy) del token antes de delegarlo a analyzer.delegate_return_node(cloned_token).
1. Constructor
Req(target_token: Token | CustomToken | str)
target_token: Miembro del enumTokenesperado,CustomTokeno cadena con el nombre del token (Token.IDENT,"IDENT", etc.).- Cada método de validación o transformación encadenado agrega una operación a
self._operationsy retornaself.
2. Métodos de Validación
Si alguna validación falla, Req deja inalterado parser.pos, reporta ReqConstraintFailed (GrammarError) y retorna None.
Comprobaciones de Mayúsculas, Minúsculas y Formato de Nombre
.is_lower(): Requiere questr(t.value).islower()seaTrue..is_upper(): Requiere questr(t.value).isupper()seaTrue..is_snake_case(): Requiereval == val.lower()y" " not in val(dondeval = str(t.value))..is_camel_case(): Requiere quevalno esté vacío,val[0].isalpha(),"_" not in valy" " not in val.
Comprobaciones de Prefijo, Sufijo y Subcadenas
.startswith(prefix: str): Requierestr(t.value).startswith(prefix)..endswith(suffix: str): Requierestr(t.value).endswith(suffix)..contains(*items: str): Requiere que cada subcadena enitemsaparezca enstr(t.value)..excludes(*items: str): Requiere que ninguna subcadena enitemsaparezca enstr(t.value).
Longitud, Límites Numéricos e Igualdad
.length(n: int): Requierelen(str(t.value)) == n..min(n: int | float):- Si
isinstance(t.value, (int, float)): requieret.value >= n. - En caso contrario: requiere
len(str(t.value)) >= n.
- Si
.max(n: int | float):- Si
isinstance(t.value, (int, float)): requieret.value <= n. - En caso contrario: requiere
len(str(t.value)) <= n.
- Si
.mindig(n: int): Requiere al menosncaracteres enstr(t.value)que cumplanchar.isdigit()..maxdig(n: int): Requiere como máximoncaracteres enstr(t.value)que cumplanchar.isdigit()..equal(expected: Any): Requierestr(t.value) == str(expected)..unequal(not_expected: Any): Requierestr(t.value) != str(not_expected).
3. Métodos de Transformación
Las transformaciones mutan cloned_token.value in-situ (sin mutar el token original en parser.tokens si un paso subsiguiente falla):
.apply_lower(): Asignat.value = str(t.value).lower()..apply_upper(): Asignat.value = str(t.value).upper()..apply_camel(): Dividestr(t.value).replace("_", " ")por espacios en blanco y une comoparts[0].lower() + "".join(x.title() for x in parts[1:])..apply_zfill(width: int): Asignat.value = str(t.value).zfill(width)..replace(old: str, new: str): Asignat.value = str(t.value).replace(old, new)..change(new_val: Any): Asignat.value = new_val..addl(prefix: str): Asignat.value = prefix + str(t.value)..addr(suffix: str): Asignat.value = str(t.value) + suffix..transform(target_fn: Callable[[Any], Any]): Asignat.value = target_fn(t.value). Sitarget_fnlanza una excepción,Reqla captura y reportaReqConstraintFailed.
4. Forma de Retorno y Diagnósticos
- Éxito: Consume el token de
parsery retornaanalyzer.delegate_return_node(cloned_token)(Identifier,LiteralNumber,LiteralString,ASTKeywordoLiteralSymbol), dondenode.token.valuecontiene el valor transformado. - Diagnósticos en Fallo:
ReqUnexpectedEOF(ParserError) siparser.empty()esTrue.ReqTokenMismatch(ParserError) si el nombre del tipo del token actual no coincide contarget_token.ReqConstraintFailed(GrammarError) si cualquier validación o paso.transform()falla.
5. Ejemplo
from gram.core.combinators import Req
from gram.core.lexer import Token
# Identificador snake_case en minúsculas de al menos 3 caracteres, normalizado a MAYÚSCULAS
const_name = (
Req(Token.IDENT)
.is_snake_case()
.min(3)
.apply_upper()
)
# Número entre 10 y 100 inclusive
port_number = Req(Token.NUMBER).min(10).max(100)