class TrampolinedGrammarInterpreter(object): """ An interpreter for OMeta grammars that processes input incrementally. """ def __init__(self, grammar, ruleName, callback=None, globals=None): self.grammar = grammar self.position = 0 self.callback = callback self.globals = globals or {} self.rules = decomposeGrammar(grammar) self.next = self.apply(ruleName, None, ()) self._localsStack = [] self.currentResult = None self.input = InputStream([], 0) self.ended = False def receive(self, buf): """ Feed data to the parser. """ if not buf: # No data. Nothing to do. return if self.ended: raise ValueError("Can't feed a parser that's been ended.") self.input.data.extend(buf) x = None for x in self.next: if x is _feed_me: return x if self.callback: self.callback(*x) self.ended = True def end(self): """ Close the input stream, indicating to the grammar that no more input will arrive. """ if self.ended: return self.ended = True x = None for x in self.next: pass if self.callback: self.callback(*x) ## Implementation note: each method, instead of being a function ## returning a value, is a generator that will yield '_feed_me' an ## arbitrary number of times, then finally yield the value of the ## expression being evaluated. def _apply(self, rule, ruleName, args): """ Apply a rule method to some args. @param rule: A method of this object. @param ruleName: The name of the rule invoked. @param args: A sequence of arguments to it. """ if args: if ((not getattr(rule, 'func_code', None)) or rule.func_code.co_argcount - 1 != len(args)): for arg in args[::-1]: self.input = ArgInput(arg, self.input) g = rule() else: g = rule(*args) for x in g: if x is _feed_me: yield x yield x return memoRec = self.input.getMemo(ruleName) if memoRec is None: oldPosition = self.input lr = LeftRecursion() memoRec = self.input.setMemo(ruleName, lr) try: inp = self.input for x in rule(): if x is _feed_me: yield x memoRec = inp.setMemo(ruleName, [x, self.input]) except ParseError: raise if lr.detected: sentinel = self.input while True: try: self.input = oldPosition for x in rule(): if x is _feed_me: yield x ans = x if (self.input == sentinel): break memoRec = oldPosition.setMemo(ruleName, [ans, self.input]) except ParseError: break self.input = oldPosition elif isinstance(memoRec, LeftRecursion): memoRec.detected = True raise self.input.nullError() self.input = memoRec[1] yield memoRec[0] def _eval(self, expr): """ Dispatch to a parse_<term name> method for the given grammar expression. """ return getattr(self, "parse_" + expr.tag.name)(*expr.args) def parse_Apply(self, ruleName, codeName, args): for x in self.apply(ruleName.data, codeName.data, args.args): if x is _feed_me: yield x yield x def apply(self, ruleName, codeName, args): """ Invoke a rule, optionally with arguments. """ argvals = [] for a in args: for x in self._eval(a): if x is _feed_me: yield x argvals.append(x[0]) _locals = {} self._localsStack.append(_locals) try: #XXX super rul = self.rules.get(ruleName) if rul: def f(): return self._eval(rul) else: #ruleName may be a Twine, so gotta call str() f = getattr(self, str('rule_' + ruleName)) for x in self._apply(f, ruleName, argvals): if x is _feed_me: yield x yield x finally: self._localsStack.pop() def parse_Exactly(self, spec): """ Accept a one or more characters that equal the given spec. """ wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: raise self.err(p.withMessage(expected(None, wanted))) yield ''.join(result), p def parse_Token(self, spec): """ Consume leading whitespace then the given string. """ val = ' ' while val.isspace(): try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val.isspace(): self.input = self.input.tail() wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: raise self.err(p.withMessage(expected("token", wanted))) yield ''.join(result), p def parse_And(self, expr): """ Execute multiple subexpressions in order, returning the result of the last one. """ seq = expr.args x = None, self.input.nullError() for subexpr in seq: for x in self._eval(subexpr): if x is _feed_me: yield x self.currentError = x[1] yield x def parse_Or(self, expr): """ Execute multiple subexpressions, returning the result of the first one that succeeds. """ errors = [] i = self.input for subexpr in expr.args: try: for x in self._eval(subexpr): if x is _feed_me: yield x val, p = x errors.append(p) self.currentError = joinErrors(errors) yield x return except ParseError, err: errors.append(err) self.input = i raise self.err(joinErrors(errors))
class TrampolinedGrammarInterpreter(object): """ An interpreter for OMeta grammars that processes input incrementally. """ def __init__(self, grammar, ruleName, callback=None, globals=None): self.grammar = grammar self.position = 0 self.callback = callback self.globals = globals or {} self.rules = decomposeGrammar(grammar) self.next = self.apply(ruleName, None, ()) self._localsStack = [] self.currentResult = None self.input = InputStream([], 0) self.ended = False def receive(self, buf): """ Feed data to the parser. """ if not buf: # No data. Nothing to do. return if self.ended: raise ValueError("Can't feed a parser that's been ended.") self.input.data.extend(buf) x = None for x in self.next: if x is _feed_me: return x if self.callback: self.callback(*x) self.ended = True def end(self): """ Close the input stream, indicating to the grammar that no more input will arrive. """ if self.ended: return self.ended = True x = None for x in self.next: pass if self.callback: self.callback(*x) def error(self, typ, val, trail=None): raise ParseError(''.join(self.input.data), typ, val, trail=trail) ## Implementation note: each method, instead of being a function ## returning a value, is a generator that will yield '_feed_me' an ## arbitrary number of times, then finally yield the value of the ## expression being evaluated. def _apply(self, rule, ruleName, args): """ Apply a rule method to some args. @param rule: A method of this object. @param ruleName: The name of the rule invoked. @param args: A sequence of arguments to it. """ if args: if ((not getattr(rule, 'func_code', None)) or rule.func_code.co_argcount - 1 != len(args)): for arg in args[::-1]: self.input = ArgInput(arg, self.input) g = rule() else: g = rule(*args) for x in g: if x is _feed_me: yield x yield x return memoRec = self.input.getMemo(ruleName) if memoRec is None: oldPosition = self.input lr = LeftRecursion() memoRec = self.input.setMemo(ruleName, lr) try: inp = self.input for x in rule(): if x is _feed_me: yield x memoRec = inp.setMemo(ruleName, [x, self.input]) except ParseError: raise if lr.detected: sentinel = self.input while True: try: self.input = oldPosition for x in rule(): if x is _feed_me: yield x ans = x if (self.input == sentinel): break memoRec = oldPosition.setMemo(ruleName, [ans, self.input]) except ParseError: break self.input = oldPosition elif isinstance(memoRec, LeftRecursion): memoRec.detected = True self.error(None, None) self.input = memoRec[1] yield memoRec[0] def _eval(self, expr): """ Dispatch to a parse_<term name> method for the given grammar expression. """ return getattr(self, "parse_" + expr.tag.name)(*expr.args) def parse_Apply(self, ruleName, codeName, args): for x in self.apply(ruleName.data, codeName.data, args.args): if x is _feed_me: yield x yield x def apply(self, ruleName, codeName, args): """ Invoke a rule, optionally with arguments. """ argvals = [] for a in args: for x in self._eval(a): if x is _feed_me: yield x argvals.append(x[0]) _locals = {} self._localsStack.append(_locals) try: #XXX super rul = self.rules.get(ruleName) if rul: def f(): return self._eval(rul) else: #ruleName may be a Twine, so gotta call str() f = getattr(self, str('rule_' + ruleName)) for x in self._apply(f, ruleName, argvals): if x is _feed_me: yield x yield x finally: self._localsStack.pop() def parse_Exactly(self, spec): """ Accept a one or more characters that equal the given spec. """ wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: self.error(''.join(result), expected(None, wanted)) yield ''.join(result), p def parse_Token(self, spec): """ Consume leading whitespace then the given string. """ val = ' ' while val.isspace(): try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val.isspace(): self.input = self.input.tail() wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: self.error(''.join(result), expected(None, wanted)) yield ''.join(result), p def parse_And(self, expr): """ Execute multiple subexpressions in order, returning the result of the last one. """ seq = expr.args x = None, self.input.nullError() for subexpr in seq: for x in self._eval(subexpr): if x is _feed_me: yield x self.currentError = x[1] yield x def parse_Or(self, expr): """ Execute multiple subexpressions, returning the result of the first one that succeeds. """ errors = [] i = self.input for subexpr in expr.args: try: for x in self._eval(subexpr): if x is _feed_me: yield x val, p = x errors.append(p) self.currentError = joinErrors(errors) yield x return except ParseError, err: errors.append(err) self.input = i self.error(*joinErrors(errors))
class TrampolinedGrammarInterpreter(object): """ An interpreter for OMeta grammars that processes input incrementally. """ def __init__(self, grammar, rule, callback=None, globals=None): self.grammar = grammar self.position = 0 self.callback = callback self.globals = globals or {} self.rules = decomposeGrammar(grammar) self.next = self.setNext(rule) self._localsStack = [] self.currentResult = None self.input = InputStream([], 0) self.ended = False self._spanStart = 0 def receive(self, buf): """ Feed data to the parser. """ if not buf: # No data. Nothing to do. return if self.ended: raise ValueError("Can't feed a parser that's been ended.") self.input.data.extend(buf) x = None for x in self.next: if x is _feed_me: return x if self.callback: self.callback(*x) self.ended = True def end(self): """ Close the input stream, indicating to the grammar that no more input will arrive. """ if self.ended: return self.ended = True x = None for x in self.next: pass if self.callback: self.callback(*x) def setNext(self, rule): if not isinstance(rule, tuple): rule = (rule, ) return self.apply(rule[0], None, rule[1:]) ## Implementation note: each method, instead of being a function ## returning a value, is a generator that will yield '_feed_me' an ## arbitrary number of times, then finally yield the value of the ## expression being evaluated. def _apply(self, rule, ruleName, args): """ Apply a rule method to some args. @param rule: A method of this object. @param ruleName: The name of the rule invoked. @param args: A sequence of arguments to it. """ if args: if ((not getattr(rule, 'func_code', None)) or rule.func_code.co_argcount - 1 != len(args)): for arg in args[::-1]: self.input = ArgInput(arg, self.input) g = rule() else: g = rule(*args) for x in g: if x is _feed_me: yield x yield x return memoRec = self.input.getMemo(ruleName) if memoRec is None: oldPosition = self.input lr = LeftRecursion() memoRec = self.input.setMemo(ruleName, lr) try: inp = self.input for x in rule(): if x is _feed_me: yield x memoRec = inp.setMemo(ruleName, [x, self.input]) except ParseError: raise if lr.detected: sentinel = self.input while True: try: self.input = oldPosition for x in rule(): if x is _feed_me: yield x ans = x if (self.input == sentinel): break memoRec = oldPosition.setMemo(ruleName, [ans, self.input]) except ParseError: break self.input = oldPosition elif isinstance(memoRec, LeftRecursion): memoRec.detected = True raise self.input.nullError() self.input = memoRec[1] yield memoRec[0] def _eval(self, expr): """ Dispatch to a parse_<term name> method for the given grammar expression. """ return getattr(self, "parse_" + expr.tag.name)(*expr.args) def parse_Apply(self, ruleName, codeName, args): for x in self.apply(ruleName.data, codeName.data, args.args): if x is _feed_me: yield x yield x def apply(self, ruleName, codeName, args): """ Invoke a rule, optionally with arguments. """ argvals = [] # we tell whether a rule is a manually set one by the codeName # if it's None, then we think it's set by setNext if codeName is None: argvals = args else: for a in args: for x in self._eval(a): if x is _feed_me: yield x argvals.append(x[0]) _locals = {} self._localsStack.append(_locals) try: #XXX super rul = self.rules.get(ruleName) if rul: def f(): return self._eval(rul) else: #ruleName may be a Twine, so gotta call str() f = getattr(self, str('rule_' + ruleName)) for x in self._apply(f, ruleName, argvals): if x is _feed_me: yield x yield x finally: self._localsStack.pop() def parse_Exactly(self, spec): """ Accept a one or more characters that equal the given spec. """ wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: raise self.err(p.withMessage(expected(None, wanted))) yield ''.join(result), p def parse_Token(self, spec): """ Consume leading whitespace then the given string. """ val = ' ' while val.isspace(): try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val.isspace(): self.input = self.input.tail() wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: raise self.err(p.withMessage(expected("token", wanted))) yield ''.join(result), p def parse_And(self, expr): """ Execute multiple subexpressions in order, returning the result of the last one. """ seq = expr.args x = None, self.input.nullError() for subexpr in seq: for x in self._eval(subexpr): if x is _feed_me: yield x self.currentError = x[1] yield x def parse_Or(self, expr): """ Execute multiple subexpressions, returning the result of the first one that succeeds. """ errors = [] i = self.input for subexpr in expr.args: try: for x in self._eval(subexpr): if x is _feed_me: yield x val, p = x errors.append(p) self.currentError = joinErrors(errors) yield x return except ParseError as err: errors.append(err) self.input = i raise self.err(joinErrors(errors)) def parse_Many(self, expr, ans=None): """ Execute an expression repeatedly until it fails to match, collecting the results into a list. Implementation of '*'. """ ans = ans or [] err = None while True: try: m = self.input for x in self._eval(expr): if x is _feed_me: yield x ans.append(x[0]) self.currentError = x[1] except ParseError as error: err = error self.input = m break yield ans, err def parse_Many1(self, expr): """ Execute an expression one or more times, collecting the results into a list. Implementation of '+'. """ for x in self._eval(expr): if x is _feed_me: yield x for x in self.parse_Many(expr, ans=[x[0]]): if x is _feed_me: yield x yield x def parse_Repeat(self, min, max, expr): """ Execute an expression between C{min} and C{max} times, collecting the results into a list. Implementation of '{}'. """ if min.tag.name == '.int.': min = min.data else: min = self._localsStack[-1][min.data] if max.tag.name == '.int.': max = max.data elif max.tag.name == 'null': max = None else: max = self._localsStack[-1][max.data] e = None if min == max == 0: yield '', None return ans = [] for i in range(min): for x in self._eval(expr): if x is _feed_me: yield x v, e = x ans.append(v) if max is not None: repeats = range(min, max) for i in repeats: try: m = self.input for x in self._eval(expr): if x is _feed_me: yield x v, e = x ans.append(v) except ParseError as err: e = err self.input = m break yield ans, e def parse_Optional(self, expr): """ Execute an expression, returning None if it fails. Implementation of '?'. """ i = self.input try: for x in self._eval(expr): if x is _feed_me: yield x yield x except ParseError: self.input = i yield (None, self.input.nullError()) def parse_Not(self, expr): """ Execute an expression, returning True if it fails and failing otherwise. Implementation of '~'. """ m = self.input try: for x in self._eval(expr): if x is _feed_me: yield x except ParseError: self.input = m yield True, self.input.nullError() else: raise self.err(self.input.nullError()) def parse_Label(self, expr, label_term): """ Execute an expression , if it fails apply the label to the exception. """ label = label_term.data try: for x in self._eval(expr): if x is _feed_me: yield x print ("^^", label) self.currentError = x[1].withMessage([("Custom Exception:", label, None)]) yield x[0], self.currentError except ParseError as e: err=e raise self.err(e.withMessage([("Custom Exception:", label, None)])) def parse_Lookahead(self, expr): """ Execute an expression, then reset the input stream to the position before execution. Implementation of '~~'. """ try: i = self.input for x in self._eval(expr): if x is _feed_me: yield x finally: self.input = i def parse_Bind(self, name, expr): """ Execute an expression and bind its result to the given name. """ for x in self._eval(expr): if x is _feed_me: yield x v, err = x if name.data: self._localsStack[-1][name.data] = v else: for n, val in zip(name.args, v): self._localsStack[-1][n.data] = val yield v, err def parse_Predicate(self, expr): """ Run a Python expression and fail if it returns False. """ for x in self._eval(expr): if x is _feed_me: yield x val, err = x if not val: raise self.err(err) else: yield True, err def parse_Action(self, expr): """ Run a Python expression, return its result. """ val = eval(expr.data, self.globals, self._localsStack[-1]) yield val, self.input.nullError() def parse_ConsumedBy(self, expr): """ Run an expression. Return the literal contents of the input stream it consumed. """ oldInput = self.input for x in self._eval(expr): if x is _feed_me: yield x slice = oldInput.data[oldInput.position:self.input.position] yield "".join(slice), x[1] def rule_anything(self): """ Match a single character. """ try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() self.input = self.input.tail() yield val, p def rule_letter(self): """ Match a single letter. """ try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val in string.ascii_letters: self.input = self.input.tail() yield val, p else: raise self.err(p.withMessage(expected("letter"))) def rule_digit(self): """ Match a digit. """ try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val in string.digits: self.input = self.input.tail() yield val, p else: raise self.err(p.withMessage(expected("digit"))) def err(self, e): e.input = ''.join(str(i) for i in e.input) raise e
class TrampolinedGrammarInterpreter(object): """ An interpreter for OMeta grammars that processes input incrementally. """ def __init__(self, grammar, rule, callback=None, globals=None): self.grammar = grammar self.position = 0 self.callback = callback self.globals = globals or {} self.rules = decomposeGrammar(grammar) self.next = self.setNext(rule) self._localsStack = [] self.currentResult = None self.input = InputStream([], 0) self.ended = False self._spanStart = 0 def receive(self, buf): """ Feed data to the parser. """ if not buf: # No data. Nothing to do. return if self.ended: raise ValueError("Can't feed a parser that's been ended.") self.input.data.extend(buf) x = None for x in self.next: if x is _feed_me: return x if self.callback: self.callback(*x) self.ended = True def end(self): """ Close the input stream, indicating to the grammar that no more input will arrive. """ if self.ended: return self.ended = True x = None for x in self.next: pass if self.callback: self.callback(*x) def setNext(self, rule): if not isinstance(rule, tuple): rule = (rule, ) return self.apply(rule[0], None, rule[1:]) ## Implementation note: each method, instead of being a function ## returning a value, is a generator that will yield '_feed_me' an ## arbitrary number of times, then finally yield the value of the ## expression being evaluated. def _apply(self, rule, ruleName, args): """ Apply a rule method to some args. @param rule: A method of this object. @param ruleName: The name of the rule invoked. @param args: A sequence of arguments to it. """ if args: if ((not getattr(rule, 'func_code', None)) or rule.func_code.co_argcount - 1 != len(args)): for arg in args[::-1]: self.input = ArgInput(arg, self.input) g = rule() else: g = rule(*args) for x in g: if x is _feed_me: yield x yield x return memoRec = self.input.getMemo(ruleName) if memoRec is None: oldPosition = self.input lr = LeftRecursion() memoRec = self.input.setMemo(ruleName, lr) try: inp = self.input for x in rule(): if x is _feed_me: yield x memoRec = inp.setMemo(ruleName, [x, self.input]) except ParseError: raise if lr.detected: sentinel = self.input while True: try: self.input = oldPosition for x in rule(): if x is _feed_me: yield x ans = x if (self.input == sentinel): break memoRec = oldPosition.setMemo(ruleName, [ans, self.input]) except ParseError: break self.input = oldPosition elif isinstance(memoRec, LeftRecursion): memoRec.detected = True raise self.input.nullError() self.input = memoRec[1] yield memoRec[0] def _eval(self, expr): """ Dispatch to a parse_<term name> method for the given grammar expression. """ return getattr(self, "parse_" + expr.tag.name)(*expr.args) def parse_Apply(self, ruleName, codeName, args): for x in self.apply(ruleName.data, codeName.data, args.args): if x is _feed_me: yield x yield x def apply(self, ruleName, codeName, args): """ Invoke a rule, optionally with arguments. """ argvals = [] # we tell whether a rule is a manually set one by the codeName # if it's None, then we think it's set by setNext if codeName is None: argvals = args else: for a in args: for x in self._eval(a): if x is _feed_me: yield x argvals.append(x[0]) _locals = {} self._localsStack.append(_locals) try: #XXX super rul = self.rules.get(ruleName) if rul: def f(): return self._eval(rul) else: #ruleName may be a Twine, so gotta call str() f = getattr(self, str('rule_' + ruleName)) for x in self._apply(f, ruleName, argvals): if x is _feed_me: yield x yield x finally: self._localsStack.pop() def parse_Exactly(self, spec): """ Accept a one or more characters that equal the given spec. """ wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: raise self.err(p.withMessage(expected(None, wanted))) yield ''.join(result), p def parse_Token(self, spec): """ Consume leading whitespace then the given string. """ val = ' ' while val.isspace(): try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val.isspace(): self.input = self.input.tail() wanted = spec.data result = [] for c in wanted: try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() result.append(val) if val == c: self.input = self.input.tail() else: raise self.err(p.withMessage(expected("token", wanted))) yield ''.join(result), p def parse_And(self, expr): """ Execute multiple subexpressions in order, returning the result of the last one. """ seq = expr.args x = None, self.input.nullError() for subexpr in seq: for x in self._eval(subexpr): if x is _feed_me: yield x self.currentError = x[1] yield x def parse_Or(self, expr): """ Execute multiple subexpressions, returning the result of the first one that succeeds. """ errors = [] i = self.input for subexpr in expr.args: try: for x in self._eval(subexpr): if x is _feed_me: yield x val, p = x errors.append(p) self.currentError = joinErrors(errors) yield x return except ParseError as err: errors.append(err) self.input = i raise self.err(joinErrors(errors)) def parse_Many(self, expr, ans=None): """ Execute an expression repeatedly until it fails to match, collecting the results into a list. Implementation of '*'. """ ans = ans or [] err = None while True: try: m = self.input for x in self._eval(expr): if x is _feed_me: yield x ans.append(x[0]) self.currentError = x[1] except ParseError as error: err = error self.input = m break yield ans, err def parse_Many1(self, expr): """ Execute an expression one or more times, collecting the results into a list. Implementation of '+'. """ for x in self._eval(expr): if x is _feed_me: yield x for x in self.parse_Many(expr, ans=[x[0]]): if x is _feed_me: yield x yield x def parse_Repeat(self, min, max, expr): """ Execute an expression between C{min} and C{max} times, collecting the results into a list. Implementation of '{}'. """ if min.tag.name == '.int.': min = min.data else: min = self._localsStack[-1][min.data] if max.tag.name == '.int.': max = max.data elif max.tag.name == 'null': max = None else: max = self._localsStack[-1][max.data] e = None if min == max == 0: yield '', None return ans = [] for i in range(min): for x in self._eval(expr): if x is _feed_me: yield x v, e = x ans.append(v) if max is not None: repeats = range(min, max) for i in repeats: try: m = self.input for x in self._eval(expr): if x is _feed_me: yield x v, e = x ans.append(v) except ParseError as err: e = err self.input = m break yield ans, e def parse_Optional(self, expr): """ Execute an expression, returning None if it fails. Implementation of '?'. """ i = self.input try: for x in self._eval(expr): if x is _feed_me: yield x yield x except ParseError: self.input = i yield (None, self.input.nullError()) def parse_Not(self, expr): """ Execute an expression, returning True if it fails and failing otherwise. Implementation of '~'. """ m = self.input try: for x in self._eval(expr): if x is _feed_me: yield x except ParseError: self.input = m yield True, self.input.nullError() else: raise self.err(self.input.nullError()) def parse_Label(self, expr, label_term): """ Execute an expression , if it fails apply the label to the exception. """ label = label_term.data try: for x in self._eval(expr): if x is _feed_me: yield x print("^^", label) self.currentError = x[1].withMessage([("Custom Exception:", label, None)]) yield x[0], self.currentError except ParseError as e: err = e raise self.err(e.withMessage([("Custom Exception:", label, None)])) def parse_Lookahead(self, expr): """ Execute an expression, then reset the input stream to the position before execution. Implementation of '~~'. """ try: i = self.input for x in self._eval(expr): if x is _feed_me: yield x finally: self.input = i def parse_Bind(self, name, expr): """ Execute an expression and bind its result to the given name. """ for x in self._eval(expr): if x is _feed_me: yield x v, err = x if name.data: self._localsStack[-1][name.data] = v else: for n, val in zip(name.args, v): self._localsStack[-1][n.data] = val yield v, err def parse_Predicate(self, expr): """ Run a Python expression and fail if it returns False. """ for x in self._eval(expr): if x is _feed_me: yield x val, err = x if not val: raise self.err(err) else: yield True, err def parse_Action(self, expr): """ Run a Python expression, return its result. """ val = eval(expr.data, self.globals, self._localsStack[-1]) yield val, self.input.nullError() def parse_ConsumedBy(self, expr): """ Run an expression. Return the literal contents of the input stream it consumed. """ oldInput = self.input for x in self._eval(expr): if x is _feed_me: yield x slice = oldInput.data[oldInput.position:self.input.position] yield "".join(slice), x[1] def rule_anything(self): """ Match a single character. """ try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() self.input = self.input.tail() yield val, p def rule_letter(self): """ Match a single letter. """ try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val in string.ascii_letters: self.input = self.input.tail() yield val, p else: raise self.err(p.withMessage(expected("letter"))) def rule_digit(self): """ Match a digit. """ try: val, p = self.input.head() except EOFError: yield _feed_me val, p = self.input.head() if val in string.digits: self.input = self.input.tail() yield val, p else: raise self.err(p.withMessage(expected("digit"))) def err(self, e): e.input = ''.join(str(i) for i in e.input) raise e