Esempio n. 1
0
    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]
Esempio n. 2
0
    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]
Esempio n. 3
0
 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
Esempio n. 4
0
 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
Esempio n. 5
0
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))
Esempio n. 6
0
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
Esempio n. 7
0
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))
Esempio n. 8
0
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