Source code for pyrser.parsing.functors

import inspect
import types
from pyrser import meta, error
from pyrser.parsing.base import BasicParser
from pyrser.parsing.node import Node
from pyrser.parsing.stream import Tag


_decorators = []


[docs]class Functor: """ Dummy Base class for all parse tree classes. common property: pt if contain a Functor ptlist if contain a list of Functor """
[docs] def do_call(self, parser: BasicParser) -> Node: pass
[docs] def __call__(self, parser: BasicParser) -> Node: global _decorators # call the begin methods in order for i in range(0, len(_decorators)): _decorators[i].begin(parser, self) # forward the call to the functor res = self.do_call(parser) # call the end methods in reverse order for i in range(len(_decorators) - 1, -1, -1): _decorators[i].end(res, parser, self) return res
[docs]class Directive2(Functor):
[docs] def __init__(self, name, param: [(object, type)], pt: Functor): Functor.__init__(self) self.name = name self.pt = pt # compose the list of value param, check type for v, t in param: if type(t) is not type: raise TypeError( "Must be pair of value and type (i.e: int, str, Node)") self.param = param
[docs] def do_call(self, parser: BasicParser) -> bool: raise TypeError("Must be rewrite before execution") return False
[docs]class RewritingRules(Functor): """ Allow to write rewriting rules that transform the AST before code generation/interpretation. """
[docs] def __init__(self, name: str=""): self.name = name
[docs]class SkipIgnore(Functor): """ Call Ignore Convention primitive functor. """
[docs] def __init__(self, convention: str=""): """TODO: Could be better to implement Directive thru functors???""" self.convention = convention
[docs] def do_call(self, parser: BasicParser) -> bool: #if len(parser._ignores) > 0: # parser._ignores[-1](parser) parser.skip_ignore() return True
[docs]class Leaf: pass
[docs]class PeekChar(Functor, Leaf): """ !!'A' bnf primitive functor. """
[docs] def __init__(self, c: str): self.char = c
[docs] def do_call(self, parser: BasicParser) -> bool: return parser.peek_char(self.char)
[docs]class PeekText(Functor, Leaf): """ !!"TXT" bnf primitive functor. """
[docs] def __init__(self, c: str): self.char = c
[docs] def do_call(self, parser: BasicParser) -> bool: return parser.peek_text(self.char)
[docs]class Text(Functor, Leaf): """ "TXT" bnf primitive functor. """
[docs] def __init__(self, txt: str): self.text = txt
[docs] def do_call(self, parser: BasicParser) -> bool: return parser.read_text(self.text)
[docs]class Char(Functor, Leaf): """ 'A' bnf primitive functor. """
[docs] def __init__(self, c: str): self.char = c
[docs] def do_call(self, parser: BasicParser) -> bool: return parser.read_char(self.char)
[docs]class Range(Functor, Leaf): """ 'A'..'Z' bnf primitive functor. """
[docs] def __init__(self, begin: str, end: str): self.begin = begin self.end = end
[docs] def do_call(self, parser: BasicParser) -> bool: return parser.read_range(self.begin, self.end)
[docs]class UntilChar(Functor, Leaf): """ ->'A' bnf primitive functor. """
[docs] def __init__(self, c: str): self.char = c
[docs] def do_call(self, parser: BasicParser) -> bool: return parser.read_until(self.char)
[docs]class Seq(Functor): """ A B C bnf primitive as a functor. """
[docs] def __init__(self, *ptlist: Functor): Functor.__init__(self) if len(ptlist) == 0: raise TypeError("Expected Functor") self.ptlist = [] for it in ptlist: if not isinstance(it, SkipIgnore): self.ptlist.append(it) self.ptlist.append(SkipIgnore()) if not isinstance(self.ptlist[0], SkipIgnore): self.ptlist.insert(0, SkipIgnore())
[docs] def __getitem__(self, idx) -> Functor: """ Hide SkipIgnore object from outside """ if idx >= 0: idx = (idx * 2) + 1 else: idx = len(self.ptlist) - ((idx + 1) * 2) - 2 return self.ptlist[idx]
[docs] def do_call(self, parser: BasicParser) -> bool: parser._stream.save_context() for pt in self.ptlist: if not pt(parser): return parser._stream.restore_context() return parser._stream.validate_context()
[docs]class Scope(Functor): """ functor to wrap SCOPE/rule directive or just []. """
[docs] def __init__(self, begin: Seq, end: Seq, pt: Seq): Functor.__init__(self) self.begin = begin self.end = end self.pt = pt
[docs] def do_call(self, parser: BasicParser) -> Node: if not self.begin(parser): return False res = self.pt(parser) if not self.end(parser): return False return res
[docs]class LookAhead(Functor): """ !!A bnf primitive as a functor. """
[docs] def __init__(self, pt: Functor): Functor.__init__(self) self.pt = pt if isinstance(self.pt, Seq): if isinstance(self.pt.ptlist[0], SkipIgnore): self.pt.ptlist.pop(0) if isinstance(self.pt.ptlist[-1], SkipIgnore): self.pt.ptlist.pop()
[docs] def do_call(self, parser: BasicParser) -> bool: parser._stream.save_context() res = self.pt(parser) parser._stream.restore_context() return res
[docs]class Neg(Functor): """ !A bnf primitive as a functor. """
[docs] def __init__(self, pt: Functor): Functor.__init__(self) self.pt = pt if isinstance(self.pt, Seq): if isinstance(self.pt.ptlist[0], SkipIgnore): self.pt.ptlist.pop(0) if isinstance(self.pt.ptlist[-1], SkipIgnore): self.pt.ptlist.pop()
[docs] def do_call(self, parser: BasicParser): parser._stream.save_context() if self.pt(parser): res = parser._stream.restore_context() return res return parser._stream.validate_context()
[docs]class Complement(Functor): """ ~A bnf primitive as a functor. """
[docs] def __init__(self, pt: Functor): Functor.__init__(self) self.pt = pt if isinstance(self.pt, Seq): if isinstance(self.pt.ptlist[0], SkipIgnore): self.pt.ptlist.pop(0) if isinstance(self.pt.ptlist[-1], SkipIgnore): self.pt.ptlist.pop()
[docs] def do_call(self, parser: BasicParser) -> bool: if parser.read_eof(): return False parser._stream.save_context() res = self.pt(parser) if not res: parser._stream.incpos() return parser._stream.validate_context() parser._stream.restore_context() return False
[docs]class Until(Functor): """ ->A bnf primitive as a functor. """
[docs] def __init__(self, pt: Functor): Functor.__init__(self) self.pt = pt if isinstance(self.pt, Seq): if isinstance(self.pt.ptlist[0], SkipIgnore): self.pt.ptlist.pop(0) if isinstance(self.pt.ptlist[-1], SkipIgnore): self.pt.ptlist.pop()
[docs] def do_call(self, parser: BasicParser) -> bool: parser._stream.save_context() while not parser.read_eof(): res = self.pt(parser) if res: return parser._stream.validate_context() parser._stream.incpos() parser._stream.restore_context() parser.undo_last_ignore() return False
[docs]class Call(Functor): """ Functor wrapping a BasicParser method call in a BNF clause. """
[docs] def __init__(self, callObject, *params): Functor.__init__(self) self.callObject = callObject self.params = params
[docs] def do_call(self, parser: BasicParser) -> Node: return self.callObject(parser, *self.params)
[docs]class CallTrue(Call): """ Functor to wrap arbitrary callable object in BNF clause. """
[docs] def do_call(self, parser: BasicParser) -> Node: self.callObject(*self.params) return True
[docs]class Capture(Functor): """ Functor to handle capture nodes. """
[docs] def __init__(self, tagname: str, pt: Functor): Functor.__init__(self) if not isinstance(tagname, str) or len(tagname) == 0: raise TypeError("Illegal tagname for capture") self.tagname = tagname self.pt = pt if isinstance(self.pt, Seq): if isinstance(self.pt.ptlist[-1], SkipIgnore): self.pt.ptlist.pop()
[docs] def do_call(self, parser: BasicParser) -> Node: if parser.begin_tag(self.tagname): # subcontext parser.push_rule_nodes() res = self.pt(parser) parser.pop_rule_nodes() if res and parser.end_tag(self.tagname): tag = parser.get_tag(self.tagname) # no bindings, wrap it in a Node instance if type(res) is bool: res = Node() # update node cache parser.tag_node(self.tagname, res) parser.rule_nodes[self.tagname] = res # forward nodes return res return False
[docs]class DeclNode(Functor): """ Functor to handle node declaration with __scope__:N. """
[docs] def __init__(self, tagname: str): Functor.__init__(self) if not isinstance(tagname, str) or len(tagname) == 0: raise TypeError("Illegal tagname for capture") self.tagname = tagname
[docs] def do_call(self, parser: BasicParser) -> Node: parser.rule_nodes[self.tagname] = Node() return True
[docs]class Bind(Functor): """ Functor to handle the binding of a resulting nodes to an existing name. """
[docs] def __init__(self, tagname: str, pt: Functor): Functor.__init__(self) if not isinstance(tagname, str) or len(tagname) == 0: raise TypeError("Illegal tagname for capture") self.tagname = tagname self.pt = pt
[docs] def do_call(self, parser: BasicParser) -> Node: res = self.pt(parser) if res: parser.bind(self.tagname, res) return res return False
[docs]class Alt(Functor): """ A | B bnf primitive as a functor. """
[docs] def __init__(self, *ptlist: Seq): Functor.__init__(self) self.ptlist = ptlist
[docs] def __getitem__(self, idx) -> Functor: return self.ptlist[idx]
[docs] def do_call(self, parser: BasicParser) -> Node: # save result of current rule parser.push_rule_nodes() for pt in self.ptlist: parser._stream.save_context() parser.push_rule_nodes() res = pt(parser) if res: parser.pop_rule_nodes() parser.pop_rule_nodes() parser._stream.validate_context() return res parser.pop_rule_nodes() parser._stream.restore_context() parser.pop_rule_nodes() return False
[docs]class RepOptional(Functor): """ []? bnf primitive as a functor. """
[docs] def __init__(self, pt: Seq): Functor.__init__(self) self.pt = pt if (isinstance(self.pt, Text) or isinstance(self.pt, Char) or isinstance(self.pt, Range) or isinstance(self.pt, Directive) ): self.pt = Seq(self.pt)
[docs] def do_call(self, parser: BasicParser) -> bool: res = self.pt(parser) if res: return res return True
[docs]class Rep0N(Functor): """ []* bnf primitive as a functor. """
[docs] def __init__(self, pt: Seq): Functor.__init__(self) self.pt = pt if (isinstance(self.pt, Text) or isinstance(self.pt, Char) or isinstance(self.pt, Range) or isinstance(self.pt, Directive) ): self.pt = Seq(self.pt)
[docs] def do_call(self, parser: BasicParser) -> bool: parser._stream.save_context() parser.push_rule_nodes() while self.pt(parser): pass parser.pop_rule_nodes() return parser._stream.validate_context()
[docs]class Rep1N(Functor): """ []+ bnf primitive as a functor. """
[docs] def __init__(self, pt: Seq): Functor.__init__(self) self.pt = pt if (isinstance(self.pt, Text) or isinstance(self.pt, Char) or isinstance(self.pt, Range) or isinstance(self.pt, Directive) ): self.pt = Seq(self.pt)
[docs] def do_call(self, parser: BasicParser) -> bool: parser._stream.save_context() parser.push_rule_nodes() if self.pt(parser): while self.pt(parser): pass parser.pop_rule_nodes() return parser._stream.validate_context() parser.pop_rule_nodes() return parser._stream.restore_context()
[docs]class Error(Functor): """ Raise an error. """
[docs] def __init__(self, msg: str, **kwargs): self.msg = msg self.kw = kwargs
[docs] def do_call(self, parser: BasicParser) -> bool: parser.diagnostic.notify( error.Severity.ERROR, self.msg, error.LocationInfo.from_stream(parser._stream, is_error=True) ) raise parser.diagnostic
[docs]class Rule(Functor, Leaf): """ Call a rule by its name. """
[docs] def __init__(self, name: str): Functor.__init__(self) self.name = name
[docs] def do_call(self, parser: BasicParser) -> Node: parser.push_rule_nodes() res = parser.eval_rule(self.name) parser.pop_rule_nodes() return res
[docs]class Hook(Functor, Leaf): """ Call a hook by his name. """
[docs] def __init__(self, name: str, param: [(object, type)]): Functor.__init__(self) self.name = name # compose the list of value param, check type for v, t in param: if type(t) is not type: raise TypeError("Must be pair of value and type (i.e: int, " "str, Node)") self.param = param
[docs] def do_call(self, parser: BasicParser) -> bool: valueparam = [] for v, t in self.param: if t is Node: if v not in parser.rule_nodes: parser.diagnostic.notify( error.Severity.ERROR, "Unknown capture variable : %s" % v, error.LocationInfo.from_stream( parser._stream, is_error=True ) ) raise parser.diagnostic valueparam.append(parser.rule_nodes[v]) elif type(v) is t: valueparam.append(v) else: raise TypeError("Type mismatch expected {} got {}".format( t, type(v))) return parser.eval_hook(self.name, valueparam)
[docs]class MetaDirectiveWrapper(type): """ metaclass of all DirectiveWrapper subclasses. ensure that begin and end exists in subclasses as method """
[docs] def __new__(metacls, name, bases, namespace): cls = type.__new__(metacls, name, bases, namespace) if 'begin' not in namespace: raise TypeError( "DirectiveWrapper %s must have a begin method" % name) if not(isinstance(namespace['begin'], types.FunctionType)): raise TypeError( "'begin' not a function class in DirectiveWrapper %s" % name) if 'end' not in namespace: raise TypeError( "DirectiveWrapper %s subclasse must have a end method" % name) if not(isinstance(namespace['end'], types.FunctionType)): raise TypeError( "'end' not a function class in DirectiveWrapper %s" % name) return cls
[docs]class DirectiveWrapper(Functor, metaclass=MetaDirectiveWrapper): """ functor to wrap begin/end directive """
[docs] def __init__(self, ): Functor.__init__(self)
[docs] def checkParam(self, params: list): if (not hasattr(self.__class__, 'begin') or not hasattr(self.__class__, 'end')): return False sbegin = inspect.signature(self.begin) send = inspect.signature(self.end) idx = 0 for param in list(sbegin.parameters.values())[1:]: if idx >= len(params) and param.default is inspect.Parameter.empty: raise RuntimeError("{}: No parameter given to begin" " method for argument {}, expected {}". format( self.__class__.__name__, idx, param.annotation)) elif (idx < len(params) and not isinstance(params[idx], param.annotation)): raise TypeError( "{}: Wrong parameter in begin method parameter {} " "expected {} got {}".format( self.__class__.__name__, idx, type(params[idx]), param.annotation)) idx += 1 idx = 0 for param in list(send.parameters.values())[1:]: if idx >= len(params) and param.default is inspect.Parameter.empty: raise RuntimeError("{}: No parameter given to end" " method for argument {}, expected {}". format( self.__class__.__name__, idx, param.annotation)) elif (idx < len(params) and not isinstance(params[idx], param.annotation)): raise TypeError( "{}: Wrong parameter in end method parameter {} " "expected {} got {}".format( self.__class__.__name__, idx, type(params[idx]), param.annotation)) idx += 1 return True
[docs] def begin(self): pass
[docs] def end(self): pass
[docs]class Directive(Functor): """ functor to wrap directive HOOKS """
[docs] def __init__(self, directive: DirectiveWrapper, param: [(object, type)], pt: Functor): Functor.__init__(self) self.directive = directive self.pt = pt # compose the list of value param, check type for v, t in param: if type(t) is not type: raise TypeError( "Must be pair of value and type (i.e: int, str, Node)") self.param = param
[docs] def do_call(self, parser: BasicParser) -> Node: valueparam = [] for v, t in self.param: if t is Node: valueparam.append(parser.rule_nodes[v]) elif type(v) is t: valueparam.append(v) else: raise TypeError( "Type mismatch expected {} got {}".format(t, type(v))) if not self.directive.checkParam(valueparam): return False if not self.directive.begin(parser, *valueparam): return False res = self.pt(parser) if not self.directive.end(parser, *valueparam): return False return res
[docs]class MetaDecoratorWrapper(type): """ metaclass of all DecoratorWrapper subclasses. ensure that begin and end exists in subclasses as method """
[docs] def __new__(metacls, name, bases, namespace): cls = type.__new__(metacls, name, bases, namespace) if 'begin' not in namespace: raise TypeError( "DirectiveWrapper %s must have a begin method" % name) if not(isinstance(namespace['begin'], types.FunctionType)): raise TypeError( "'begin' not a function class in DirectiveWrapper %s" % name) if 'end' not in namespace: raise TypeError( "DirectiveWrapper %s subclasse must have a end method" % name) if not(isinstance(namespace['end'], types.FunctionType)): raise TypeError( "'end' not a function class in DirectiveWrapper %s" % name) return cls
[docs]class DecoratorWrapper(Functor, metaclass=MetaDecoratorWrapper):
[docs] def begin(self): pass
[docs] def end(self): pass
[docs]class Decorator(Functor):
[docs] def __init__(self, decoratorClass: type, param: [(object, type)], pt: Functor): self.decorator_class = decoratorClass self.pt = pt # compose the list of value param, check type for v, t in param: if type(t) is not type: raise TypeError( "Must be pair of value and type (i.e: int, str, Node)") self.param = param
[docs] def checkParam(self, the_class: type, params: list) -> bool: sinit = inspect.signature(the_class.__init__) idx = 0 for param in list(sinit.parameters.values())[1:]: if idx >= len(params) and param.default is inspect.Parameter.empty: raise RuntimeError("{}: No parameter given to begin" " method for argument {}, expected {}". format( the_class.__name__, idx, param.annotation)) elif (idx < len(params) and not isinstance(params[idx], param.annotation)): raise TypeError( "{}: Wrong parameter in begin method parameter {} " "expected {} got {}".format( the_class.__name__, idx, type(params[idx]), param.annotation)) idx += 1 return True
[docs] def do_call(self, parser: BasicParser) -> Node: """ The Decorator call is the one that actually pushes/pops the decorator in the active decorators list (parsing._decorators) """ valueparam = [] for v, t in self.param: if t is Node: valueparam.append(parser.rule_nodes[v]) elif type(v) is t: valueparam.append(v) else: raise TypeError( "Type mismatch expected {} got {}".format(t, type(v))) if not self.checkParam(self.decorator_class, valueparam): return False decorator = self.decorator_class(*valueparam) global _decorators _decorators.append(decorator) res = self.pt(parser) _decorators.pop() return res