Source code for pyrser.dsl

from pyrser import parsing
from pyrser import meta
from pyrser import error
from pyrser.directives import ignore


[docs]class EBNF(parsing.Parser): """ Basic class for BNF DSL PARSING. A full parser for the BNF is provided by this class. We construct a tree to represents, thru functors, BNF semantics. """
[docs] def get_rules(self) -> parsing.Node: """ Parse the DSL and provide a dictionnaries of all resulting rules. Call by the MetaGrammar class. TODO: could be done in the rules property of parsing.BasicParser??? """ res = None try: res = self.eval_rule('bnf_dsl') if not res: # we fail to parse, but error is not set self.diagnostic.notify( error.Severity.ERROR, "Parse error in '%s' in EBNF bnf" % self._lastRule, error.LocationInfo.from_maxstream(self._stream) ) raise self.diagnostic except error.Diagnostic as d: d.notify( error.Severity.ERROR, "Parse error in '%s' in EBNF bnf" % self._lastRule ) raise d return res
@property def rules(self) -> dict: print("USE rules PROPERTY") return self._rules
[docs] def __init__(self, content='', sname=None): """ Define the DSL parser. """ super().__init__(content, sname) self.set_rules({ # # bnf_dsl = [ @ignore("C/C++") bnf_stmts ] # //todo: bnf_dsl = [ @ignore("C/C++") [bnf_stmts] eof ] # 'bnf_dsl': parsing.Seq( # tree is not already construct but Directive need it # forward it thru a lambda parsing.Directive(ignore.Ignore(), [("C/C++", str)], lambda parser: self.__class__._rules['bnf_stmts'](parser)), ), # # bnf_stmts = [ [rule : r #add_rules(_, r) ]+ Base.eof ] # //todo: bnf_stmts = [ [rule : r #add_rules(_, r) ]+] # 'bnf_stmts': parsing.Seq( parsing.Rep1N(parsing.Seq( parsing.Capture("r", parsing.Rule('rule')), parsing.Hook('add_rules', [("_", parsing.Node), ("r", parsing.Node)]) )), parsing.Rule('Base.eof') ), # TODO: add directive hooks / change ns_name by def_rule # # rule = [ ns_name : rn '=' '[' alternatives : alts # #add_rule(_, rn, alts) ']' ] # 'rule': parsing.Seq( parsing.Capture("rn", parsing.Rule('ns_name')), parsing.Alt( parsing.Char("="), parsing.Error("Expected '='")), parsing.Alt( parsing.Char("["), parsing.Error("Expected '['")), parsing.Capture("alts", parsing.Rule('alternatives')), parsing.Hook('add_rule', [("_", parsing.Node), ("rn", parsing.Node), ("alts", parsing.Node)]), parsing.Alt( #parsing.Call(parsing.Parser.read_char, ']'), parsing.Char(']'), parsing.Error("Expected ']'")) ), # # alternatives = # [ # sequences : alt #add_alt(_, alt) # ['|' sequences : alt #add_alt(_, alt) ]* # ] # 'alternatives': parsing.Seq( parsing.Capture('alt', parsing.Rule('sequences')), parsing.Hook('add_alt', [("_", parsing.Node), ("alt", parsing.Node)]), parsing.Rep0N( parsing.Seq( parsing.Char('|'), parsing.Capture('alt', parsing.Rule('sequences')), parsing.Hook('add_alt', [("_", parsing.Node), ("alt", parsing.Node)]) ) ) ), # # sequences = [ [ sequence : cla #add_sequences(_, cla) ]+ ] # 'sequences': parsing.Rep1N( parsing.Seq( parsing.Capture('cla', parsing.Rule('sequence')), parsing.Hook('add_sequences', [("_", parsing.Node), ("cla", parsing.Node)]) ) ), # # sequence = [ # [ # [ '~' | "!!" | '!' | "->" ]?: mod # [ ns_name : rid #add_ruleclause_name(_, rid) # | Base.string : txt #add_text(_, txt) # | Base.char : begin ".." Base.char : end # #add_range(_, begin, end) # | Base.char : c #add_char(_, c) # | '[' alternatives : subsequence ']' # #add_subsequence(_, subsequence) # ] #add_mod(_, mod) # [ repeat : rpt #add_rpt(_, mod, rpt) ]? # | hook : h #add_hook(_, h) # | directive2 : d sequences : s #add_directive2(_, d, s) # | directive : d sequences : s #add_directive(_, d, s) # ] # [ # ":>" Base.id : bind #add_bind(_, bind) # | ':' Base.id : cpt #add_capture(_, cpt) # ]? # ] # 'sequence': parsing.Seq( parsing.Alt( parsing.Seq( parsing.Capture( 'mod', parsing.RepOptional( parsing.Alt( parsing.Char('~'), parsing.Text('!!'), parsing.Char('!'), parsing.Text('->') ) ) ), parsing.Alt( parsing.Seq( parsing.Capture( 'rid', parsing.Rule('ns_name') ), parsing.Hook('add_ruleclause_name', [("_", parsing.Node), ("rid", parsing.Node)]) ), parsing.Seq( parsing.Capture('txt', parsing.Rule('Base.string')), parsing.Hook('add_text', [("_", parsing.Node), ("txt", parsing.Node)]) ), parsing.Seq( parsing.Capture('begin', parsing.Rule('Base.char')), parsing.Text(".."), parsing.Capture( 'end', parsing.Rule('Base.char') ), parsing.Hook('add_range', [("_", parsing.Node), ("begin", parsing.Node), ("end", parsing.Node)]) ), parsing.Seq( parsing.Capture( 'c', parsing.Rule('Base.char') ), parsing.Hook('add_char', [("_", parsing.Node), ("c", parsing.Node)]) ), parsing.Seq( parsing.Char('['), parsing.Capture( 'subsequence', parsing.Alt( parsing.Rule('alternatives'), parsing.Error("Expected sequences"))), parsing.Alt( parsing.Char(']'), parsing.Error("Expected ']'")), parsing.Hook('add_subsequence', [("_", parsing.Node), ("subsequence", parsing.Node)]), ) ), parsing.Hook('add_mod', [("_", parsing.Node), ("mod", parsing.Node)]), parsing.RepOptional( parsing.Seq( parsing.Capture( 'rpt', parsing.Rule('repeat') ), parsing.Hook('add_rpt', [("_", parsing.Node), ("mod", parsing.Node), ("rpt", parsing.Node)]) ) ), ), parsing.Seq( parsing.Capture('h', parsing.Rule('hook')), parsing.Hook('add_hook', [('_', parsing.Node), ('h', parsing.Node)]) ), parsing.Seq( parsing.Capture('d', parsing.Rule('directive2')), parsing.Capture('s', parsing.Rule('sequences')), parsing.Hook('add_directive2', [('_', parsing.Node), ('d', parsing.Node), ('s', parsing.Node)]) ), parsing.Seq( parsing.Capture('d', parsing.Rule('directive')), parsing.Capture('s', parsing.Rule('sequences')), parsing.Hook('add_directive', [('_', parsing.Node), ('d', parsing.Node), ('s', parsing.Node)]) ) ), parsing.RepOptional( parsing.Alt( parsing.Seq( parsing.Text(':>'), parsing.Capture( 'bind', parsing.Rule('Base.id')), parsing.Hook('add_bind', [('_', parsing.Node), ('bind', parsing.Node)]) ), parsing.Seq( parsing.Char(':'), parsing.Capture( 'cpt', parsing.Rule('Base.id')), parsing.Hook('add_capture', [('_', parsing.Node), ('cpt', parsing.Node)]) ) ) ) ), # ns_name = [ [@ignore("null") [ Base.id ['.' Base.id]* ]]: rid ] # 'ns_name': parsing.Capture( 'rid', parsing.Scope( parsing.Call(parsing.Parser.push_ignore, parsing.Parser.ignore_null), parsing.Call(parsing.Parser.pop_ignore), parsing.Seq( parsing.Rule('Base.id'), parsing.Rep0N( parsing.Seq( parsing.Char('.'), parsing.Alt( parsing.Rule('Base.id'), parsing.Error( "Expected identifier after '.'")) ) ) ) ) ), # # repeat = [ '?' #add_optional(_) # | '*' #add_0N(_) # | '+' #add_1N(_) # ] # 'repeat': parsing.Alt( parsing.Seq( parsing.Char('?'), parsing.Hook('add_optional', [("_", parsing.Node)]) ), parsing.Seq( parsing.Char('*'), parsing.Hook('add_0N', [("_", parsing.Node)]) ), parsing.Seq( parsing.Char('+'), parsing.Hook('add_1N', [("_", parsing.Node)]) ), ), # # hook = [ '#' ns_name : n #hook_name(_, n) # ['(' param : p #hook_param(_, p) # [',' param : p #hook_param(_, p)]* # ')']? # ] # 'hook': parsing.Seq( parsing.Char('#'), parsing.Capture('n', parsing.Rule('ns_name')), parsing.Hook('hook_name', [('_', parsing.Node), ('n', parsing.Node)]), parsing.RepOptional( parsing.Seq( parsing.Char('('), parsing.Capture( 'p', parsing.Alt( parsing.Rule('param'), parsing.Error("Expected parameter"))), parsing.Hook('hook_param', [('_', parsing.Node), ('p', parsing.Node)]), parsing.Rep0N( parsing.Seq( parsing.Char(','), parsing.Capture( 'p', parsing.Alt( parsing.Rule('param'), parsing.Error("Expected parameter"))), parsing.Hook('hook_param', [('_', parsing.Node), ('p', parsing.Node)])) ), parsing.Alt( parsing.Char(')'), parsing.Error("Expected ')'")) ) ), ), # # directive2 = [ '$' ns_name : n #hook_name(_, n) # ['(' param : p #hook_param(_, p) # [',' param : p #hook_param(_, p)]* # ')']? # ] 'directive2': parsing.Seq( parsing.Char('$'), parsing.Capture('n', parsing.Rule('ns_name')), parsing.Hook('hook_name', [('_', parsing.Node), ('n', parsing.Node)]), parsing.RepOptional( parsing.Seq( parsing.Char('('), parsing.Capture( 'p', parsing.Alt( parsing.Rule('param'), parsing.Error("Expected parameter"))), parsing.Hook('hook_param', [('_', parsing.Node), ('p', parsing.Node)]), parsing.Rep0N( parsing.Seq( parsing.Char(','), parsing.Capture( 'p', parsing.Alt( parsing.Rule('param'), parsing.Error("Expected parameter"))), parsing.Hook('hook_param', [('_', parsing.Node), ('p', parsing.Node)]), ) ), parsing.Alt( parsing.Char(')'), parsing.Error("Expected ')'")) ) ), ), # # directive = [ '@' ns_name : n #hook_name(_, n) # ['(' param : p #hook_param(_, p) # [',' param : p #hook_param(_, p)]* # ')']? # ] 'directive': parsing.Seq( parsing.Char('@'), parsing.Capture('n', parsing.Rule('ns_name')), parsing.Hook('hook_name', [('_', parsing.Node), ('n', parsing.Node)]), parsing.RepOptional( parsing.Seq( parsing.Char('('), parsing.Capture( 'p', parsing.Alt( parsing.Rule('param'), parsing.Error("Expected parameter"))), parsing.Hook('hook_param', [('_', parsing.Node), ('p', parsing.Node)]), parsing.Rep0N( parsing.Seq( parsing.Char(','), parsing.Capture( 'p', parsing.Alt( parsing.Rule('param'), parsing.Error("Expected parameter"))), parsing.Hook('hook_param', [('_', parsing.Node), ('p', parsing.Node)]), ) ), parsing.Alt( parsing.Char(')'), parsing.Error("Expected ')'")) ) ), ), # # param = [ Base.num :n #param_num(_, n) # | Base.string : s #param_str(_, s) # | Base.char : c #param_char(_, c) # | ns_name : i #param_id(_, i) # ] # 'param': parsing.Alt( parsing.Seq( parsing.Capture('n', parsing.Rule('Base.num')), parsing.Hook('param_num', [('_', parsing.Node), ('n', parsing.Node)]) ), parsing.Seq( parsing.Capture('s', parsing.Rule('Base.string')), parsing.Hook('param_str', [('_', parsing.Node), ('s', parsing.Node)]) ), parsing.Seq( parsing.Capture('c', parsing.Rule('Base.char')), parsing.Hook('param_char', [('_', parsing.Node), ('c', parsing.Node)]) ), parsing.Seq( parsing.Capture('i', parsing.Rule('ns_name')), parsing.Hook('param_id', [('_', parsing.Node), ('i', parsing.Node)]) ), ), }) # Hooks part # ---------- # All these functions are automatically added to class EBNF
@meta.hook(EBNF, "EBNF.add_mod") def add_mod(self, seq, mod): """Create a tree.{Complement, LookAhead, Neg, Until}""" modstr = self.value(mod) if modstr == '~': seq.parser_tree = parsing.Complement(seq.parser_tree) elif modstr == '!!': seq.parser_tree = parsing.LookAhead(seq.parser_tree) elif modstr == '!': seq.parser_tree = parsing.Neg(seq.parser_tree) elif modstr == '->': seq.parser_tree = parsing.Until(seq.parser_tree) return True @meta.hook(EBNF, "EBNF.add_ruleclause_name") def add_ruleclause_name(self, ns_name, rid) -> bool: """Create a tree.Rule""" ns_name.parser_tree = parsing.Rule(self.value(rid)) return True @meta.hook(EBNF, "EBNF.add_rules") def add_rules(self, bnf, r) -> bool: """Attach a parser tree to the dict of rules""" bnf[r.rulename] = r.parser_tree return True @meta.hook(EBNF, "EBNF.add_rule") def add_rule(self, rule, rn, alts) -> bool: """Add the rule name""" rule.rulename = self.value(rn) rule.parser_tree = alts.parser_tree return True @meta.hook(EBNF, "EBNF.add_sequences") def add_sequences(self, sequences, cla) -> bool: """Create a tree.Seq""" if not hasattr(sequences, 'parser_tree'): # forward sublevel of sequence as is sequences.parser_tree = cla.parser_tree else: oldnode = sequences if isinstance(oldnode.parser_tree, parsing.Seq): oldpt = list(oldnode.parser_tree.ptlist) else: oldpt = [oldnode.parser_tree] oldpt.append(cla.parser_tree) sequences.parser_tree = parsing.Seq(*tuple(oldpt)) return True @meta.hook(EBNF, "EBNF.add_alt") def add_alt(self, alternatives, alt) -> bool: """Create a tree.Alt""" if not hasattr(alternatives, 'parser_tree'): # forward sublevel of alt as is if hasattr(alt, 'parser_tree'): alternatives.parser_tree = alt.parser_tree else: alternatives.parser_tree = alt else: oldnode = alternatives if isinstance(oldnode.parser_tree, parsing.Alt): oldpt = list(oldnode.parser_tree.ptlist) else: oldpt = [oldnode.parser_tree] oldpt.append(alt.parser_tree) alternatives.parser_tree = parsing.Alt(*tuple(oldpt)) return True @meta.hook(EBNF, "EBNF.add_char") def add_char(self, sequence, c): """Add a read_char primitive""" sequence.parser_tree = parsing.Char(self.value(c).strip("'")) return True @meta.hook(EBNF, "EBNF.add_text") def add_text(self, sequence, txt): """Add a read_text primitive""" sequence.parser_tree = parsing.Text(self.value(txt).strip('"')) return True @meta.hook(EBNF, "EBNF.add_range") def add_range(self, sequence, begin, end): """Add a read_range primitive""" sequence.parser_tree = parsing.Range(self.value(begin).strip("'"), self.value(end).strip("'")) return True @meta.hook(EBNF, "EBNF.add_rpt") def add_rpt(self, sequence, mod, pt): """Add a repeater to the previous sequence""" modstr = self.value(mod) if modstr == '!!': # cursor on the REPEATER self._stream.restore_context() # log the error self.diagnostic.notify( error.Severity.ERROR, "Cannot repeat a lookahead rule", error.LocationInfo.from_stream(self._stream, is_error=True) ) raise self.diagnostic if modstr == '!': # cursor on the REPEATER self._stream.restore_context() # log the error self.diagnostic.notify( error.Severity.ERROR, "Cannot repeat a negated rule", error.LocationInfo.from_stream(self._stream, is_error=True) ) raise self.diagnostic oldnode = sequence sequence.parser_tree = pt.functor(oldnode.parser_tree) return True @meta.hook(EBNF, "EBNF.add_capture") def add_capture(self, sequence, cpt): """Create a tree.Capture""" cpt_value = self.value(cpt) sequence.parser_tree = parsing.Capture(cpt_value, sequence.parser_tree) return True @meta.hook(EBNF, "EBNF.add_bind") def add_bind(self, sequence, cpt): """Create a tree.Bind""" cpt_value = self.value(cpt) sequence.parser_tree = parsing.Bind(cpt_value, sequence.parser_tree) return True @meta.hook(EBNF, "EBNF.add_subsequence") def add_subsequence(self, sequence, subsequence): """Add a subsequence into a sequence""" sequence.parser_tree = subsequence.parser_tree return True @meta.hook(EBNF, "EBNF.add_optional") def add_optional(self, repeat): """Create a tree.RepOptional""" repeat.functor = parsing.RepOptional return True @meta.hook(EBNF, "EBNF.add_0N") def add_0N(self, repeat): """Create a tree.Rep0N""" repeat.functor = parsing.Rep0N return True @meta.hook(EBNF, "EBNF.add_1N") def add_1N(self, repeat): """Create a tree.Rep1N""" repeat.functor = parsing.Rep1N return True @meta.hook(EBNF, "EBNF.add_hook") def add_hook(self, sequence, h): """Create a tree.Hook""" sequence.parser_tree = parsing.Hook(h.name, h.listparam) return True @meta.hook(EBNF, "EBNF.param_num") def param_num(self, param, n): """Parse a int in parameter list""" param.pair = (int(self.value(n)), int) return True @meta.hook(EBNF, "EBNF.param_str") def param_str(self, param, s): """Parse a str in parameter list""" param.pair = (self.value(s).strip('"'), str) return True @meta.hook(EBNF, "EBNF.param_char") def param_char(self, param, c): """Parse a char in parameter list""" param.pair = (self.value(c).strip("'"), str) return True @meta.hook(EBNF, "EBNF.param_id") def param_id(self, param, i): """Parse a node name in parameter list""" param.pair = (self.value(i), parsing.Node) return True @meta.hook(EBNF, "EBNF.hook_name") def hook_name(self, hook, n): """Parse a hook name""" hook.name = self.value(n) hook.listparam = [] return True @meta.hook(EBNF, "EBNF.hook_param") def hook_param(self, hook, p): """Parse a hook parameter""" hook.listparam.append(p.pair) return True @meta.hook(EBNF, "EBNF.add_directive2") def add_directive2(self, sequence, d, s): """Add a directive in the sequence""" sequence.parser_tree = parsing.Directive2( d.name, d.listparam, s.parser_tree ) return True @meta.hook(EBNF, "EBNF.add_directive") def add_directive(self, sequence, d, s): """Add a directive in the sequence""" if d.name in meta._directives: the_class = meta._directives[d.name] sequence.parser_tree = parsing.Directive(the_class(), d.listparam, s.parser_tree) elif d.name in meta._decorators: the_class = meta._decorators[d.name] sequence.parser_tree = parsing.Decorator(the_class, d.listparam, s.parser_tree) else: raise TypeError("Unkown directive or decorator %s" % d.name) return True