Files
Django_kube/Webcal-python/venv/lib/python3.11/site-packages/tatsu/parser_semantics.py
T

149 lines
4.1 KiB
Python

from __future__ import annotations
from collections.abc import Sequence
from . import grammars
from .exceptions import FailedSemantics
from .semantics import ModelBuilderSemantics
from .util import eval_escapes, flatten, re, warning
class EBNFGrammarSemantics(ModelBuilderSemantics):
def __init__(self, grammar_name):
super().__init__(
base_type=grammars.Model, types=grammars.Model.classes(),
)
self.grammar_name = grammar_name
self.rules = {}
def token(self, ast, *args):
token = ast
if not token:
raise FailedSemantics('empty token')
return grammars.Token(token)
def pattern(self, ast, *args):
return grammars.Pattern(ast)
def regexes(self, ast, *args):
pattern = ''.join(ast)
try:
re.compile(pattern)
except (TypeError, re.error) as e:
raise FailedSemantics('regexp error: ' + str(e)) from e
return ast
def regex(self, ast, *args):
pattern = ast
try:
re.compile(pattern)
except (TypeError, re.error) as e:
raise FailedSemantics('regexp error: ' + str(e)) from e
return pattern
def string(self, ast):
return eval_escapes(ast)
def hex(self, ast):
return int(ast, 16)
def float(self, ast):
return float(ast)
def int(self, ast):
return int(ast)
def null(self, ast):
return None
def cut_deprecated(self, ast, *args):
warning(
'The use of >> for cut is deprecated. Use the ~ symbol instead.',
)
return grammars.Cut()
def override_single_deprecated(self, ast, *args):
warning('The use of @ for override is deprecated. Use @: instead')
return grammars.Override(ast)
def sequence(self, ast, *args):
seq = ast.sequence
assert isinstance(seq, Sequence), str(seq)
if len(seq) == 1:
return seq[0]
return grammars.Sequence(ast)
def choice(self, ast, *args):
if len(ast) == 1:
return ast[0]
return grammars.Choice(ast)
def new_name(self, name):
if name in self.rules:
raise FailedSemantics(f'rule "{name!s}" already defined')
return name
def known_name(self, name):
if name not in self.rules:
raise FailedSemantics(f'rule "{name!s}" not yet defined')
return name
def boolean(self, ast):
return str(ast).lower() in {'true', 'yes', 'ok', '1'}
def rule(self, ast, *args):
decorators = ast.decorators
name = ast.name
exp = ast.exp
base = ast.base
params = ast.params
kwparams = dict(ast.kwparams) if ast.kwparams else None
if 'override' not in decorators and name in self.rules:
self.new_name(name)
elif 'override' in decorators:
self.known_name(name)
if not base:
rule = grammars.Rule(
ast, name, exp, params, kwparams, decorators=decorators,
)
else:
self.known_name(base)
base_rule = self.rules[base]
rule = grammars.BasedRule(
ast,
name,
exp,
base_rule,
params,
kwparams,
decorators=decorators,
)
self.rules[name] = rule
return rule
def rule_include(self, ast, *args):
name = str(ast)
self.known_name(name)
rule = self.rules[name]
return grammars.RuleInclude(rule)
def grammar(self, ast, *args):
directives = {d.name: d.value for d in flatten(ast.directives)}
keywords = list(flatten(ast.keywords)) or []
if directives.get('whitespace') in {'None', 'False'}:
# NOTE: use '' because None will _not_ override defaults in configuration
directives['whitespace'] = ''
name = self.grammar_name or directives.get('grammar')
return grammars.Grammar(
name,
list(self.rules.values()),
directives=directives,
keywords=keywords,
)