grammar.py revision 6501
16501Snate@binkert.org# Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org>
26501Snate@binkert.org# All rights reserved.
36501Snate@binkert.org#
46501Snate@binkert.org# Redistribution and use in source and binary forms, with or without
56501Snate@binkert.org# modification, are permitted provided that the following conditions are
66501Snate@binkert.org# met: redistributions of source code must retain the above copyright
76501Snate@binkert.org# notice, this list of conditions and the following disclaimer;
86501Snate@binkert.org# redistributions in binary form must reproduce the above copyright
96501Snate@binkert.org# notice, this list of conditions and the following disclaimer in the
106501Snate@binkert.org# documentation and/or other materials provided with the distribution;
116501Snate@binkert.org# neither the name of the copyright holders nor the names of its
126501Snate@binkert.org# contributors may be used to endorse or promote products derived from
136501Snate@binkert.org# this software without specific prior written permission.
146501Snate@binkert.org#
156501Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
166501Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
176501Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
186501Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
196501Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
206501Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
216501Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
226501Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
236501Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
246501Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
256501Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
266501Snate@binkert.org
276501Snate@binkert.orgfrom ply import lex, yacc
286501Snate@binkert.org
296501Snate@binkert.orgclass TokenError(lex.LexError):
306501Snate@binkert.org    def __init__(self, msg, t):
316501Snate@binkert.org        super(TokenError, self).__init__(msg)
326501Snate@binkert.org        self.token = t
336501Snate@binkert.org
346501Snate@binkert.orgclass ParseError(yacc.YaccError):
356501Snate@binkert.org    def __init__(self, message, token=None):
366501Snate@binkert.org        super(ParseError, self).__init__(message)
376501Snate@binkert.org        self.token = token
386501Snate@binkert.org
396501Snate@binkert.orgclass Tokenizer(object):
406501Snate@binkert.org    def __init__(self, lexer, data):
416501Snate@binkert.org        if isinstance(data, basestring):
426501Snate@binkert.org            indata = [ data ]
436501Snate@binkert.org        elif isinstance(data, file):
446501Snate@binkert.org            indata = data.xreadlines()
456501Snate@binkert.org        else:
466501Snate@binkert.org            indata = data
476501Snate@binkert.org
486501Snate@binkert.org        def _input():
496501Snate@binkert.org            for i,line in enumerate(indata):
506501Snate@binkert.org                lexer.lineno = i + 1
516501Snate@binkert.org                lexer.input(line)
526501Snate@binkert.org                while True:
536501Snate@binkert.org                    tok = lexer.token()
546501Snate@binkert.org                    if not tok:
556501Snate@binkert.org                        break
566501Snate@binkert.org                    yield tok
576501Snate@binkert.org        self.input = _input()
586501Snate@binkert.org
596501Snate@binkert.org    def next(self):
606501Snate@binkert.org        return self.input.next()
616501Snate@binkert.org
626501Snate@binkert.org    def __iter__(self):
636501Snate@binkert.org        return self
646501Snate@binkert.org
656501Snate@binkert.org    def token(self):
666501Snate@binkert.org        try:
676501Snate@binkert.org            return self.next()
686501Snate@binkert.org        except StopIteration:
696501Snate@binkert.org            return None
706501Snate@binkert.org
716501Snate@binkert.orgclass Grammar(object):
726501Snate@binkert.org    def __init__(self, output=None, debug=False):
736501Snate@binkert.org        self.yacc_args = {}
746501Snate@binkert.org        self.yacc_args['debug'] = debug
756501Snate@binkert.org
766501Snate@binkert.org        if output:
776501Snate@binkert.org            import os
786501Snate@binkert.org
796501Snate@binkert.org            dir,tab = os.path.split(output)
806501Snate@binkert.org            if not tab.endswith('.py'):
816501Snate@binkert.org                raise AttributeError, 'The output file must end with .py'
826501Snate@binkert.org            self.yacc_args['outputdir'] = dir
836501Snate@binkert.org            self.yacc_args['tabmodule'] = tab[:-3]
846501Snate@binkert.org
856501Snate@binkert.org    def t_error(self, t):
866501Snate@binkert.org        raise lex.LexError("Illegal character %s @ %d:%d" % \
876501Snate@binkert.org              (`t.value[0]`, t.lineno, t.lexpos), `t.value[0]`)
886501Snate@binkert.org
896501Snate@binkert.org    def p_error(self, t):
906501Snate@binkert.org        if t:
916501Snate@binkert.org            msg = "Syntax error at %d:%d\n>>%s<<" % \
926501Snate@binkert.org                  (t.lineno, t.lexpos + 1, t.value)
936501Snate@binkert.org        else:
946501Snate@binkert.org            msg = "Syntax error at end of input"
956501Snate@binkert.org        raise ParseError(msg, t)
966501Snate@binkert.org
976501Snate@binkert.org    def __getattr__(self, attr):
986501Snate@binkert.org        if attr == 'parser':
996501Snate@binkert.org            import ply.yacc
1006501Snate@binkert.org            parser = ply.yacc.yacc(module=self, **self.yacc_args)
1016501Snate@binkert.org            self.parser = parser
1026501Snate@binkert.org            return parser
1036501Snate@binkert.org
1046501Snate@binkert.org        if attr == 'lexer':
1056501Snate@binkert.org            import ply.lex
1066501Snate@binkert.org            lexer = ply.lex.lex(module=self)
1076501Snate@binkert.org            self.lexer = lexer
1086501Snate@binkert.org            return lexer
1096501Snate@binkert.org
1106501Snate@binkert.org        raise AttributeError, "'%s' object has no attribute '%s'" % \
1116501Snate@binkert.org              (self.__class__.__name__, attr)
1126501Snate@binkert.org
1136501Snate@binkert.org    def parse(self, stmt, **kwargs):
1146501Snate@binkert.org        self.lexer.lineno = 1
1156501Snate@binkert.org        result = self.parser.parse(lexer=Tokenizer(self.lexer, stmt), **kwargs)
1166501Snate@binkert.org        self.parser.restart()
1176501Snate@binkert.org
1186501Snate@binkert.org        return result
1196501Snate@binkert.org
120