# Copyright (c) 2006-2009 Nathan Binkert # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from ply import lex, yacc class TokenError(lex.LexError): def __init__(self, msg, t): super(TokenError, self).__init__(msg) self.token = t class ParseError(yacc.YaccError): def __init__(self, message, token=None): super(ParseError, self).__init__(message) self.token = token class Tokenizer(object): def __init__(self, lexer, data): if isinstance(data, basestring): indata = [ data ] elif isinstance(data, file): indata = data.xreadlines() else: indata = data def _input(): for i,line in enumerate(indata): lexer.lineno = i + 1 lexer.input(line) while True: tok = lexer.token() if not tok: break yield tok self.input = _input() self.lexer = lexer def next(self): return self.input.next() def __iter__(self): return self def token(self): try: return self.next() except StopIteration: return None def __getattr__(self, attr): return getattr(self.lexer, attr) class Grammar(object): def __init__(self, output=None, debug=False): self.yacc_args = {} self.yacc_args['debug'] = debug if output: import os dir,tab = os.path.split(output) if not tab.endswith('.py'): raise AttributeError, 'The output file must end with .py' self.yacc_args['outputdir'] = dir self.yacc_args['tabmodule'] = tab[:-3] def t_error(self, t): raise lex.LexError("Illegal character %s @ %d:%d" % \ (`t.value[0]`, t.lineno, t.lexpos), `t.value[0]`) def p_error(self, t): if t: msg = "Syntax error at %d:%d\n>>%s<<" % \ (t.lineno, t.lexpos + 1, t.value) else: msg = "Syntax error at end of input" raise ParseError(msg, t) def __getattr__(self, attr): if attr == 'parser': import ply.yacc parser = ply.yacc.yacc(module=self, **self.yacc_args) self.parser = parser return parser if attr == 'lexer': import ply.lex lexer = ply.lex.lex(module=self) self.lexer = lexer return lexer raise AttributeError, "'%s' object has no attribute '%s'" % \ (self.__class__.__name__, attr) def parse(self, stmt, **kwargs): self.lexer.lineno = 1 result = self.parser.parse(lexer=Tokenizer(self.lexer, stmt), **kwargs) self.parser.restart() return result