1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
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,
)
|