18452Snate@binkert.org# Copyright (c) 2006-2011 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
278452Snate@binkert.orgimport os
2813720Sandreas.sandberg@arm.comfrom six import string_types
296501Snate@binkert.org
308452Snate@binkert.orgimport ply.lex
318452Snate@binkert.orgimport ply.yacc
326501Snate@binkert.org
338452Snate@binkert.orgclass ParseError(Exception):
346501Snate@binkert.org    def __init__(self, message, token=None):
358452Snate@binkert.org        Exception.__init__(self, message)
366501Snate@binkert.org        self.token = token
376501Snate@binkert.org
388452Snate@binkert.orgclass Grammar(object):
398452Snate@binkert.org    def setupLexerFactory(self, **kwargs):
408452Snate@binkert.org        if 'module' in kwargs:
4113663Sandreas.sandberg@arm.com            raise AttributeError("module is an illegal attribute")
428452Snate@binkert.org        self.lex_kwargs = kwargs
436501Snate@binkert.org
448452Snate@binkert.org    def setupParserFactory(self, **kwargs):
458452Snate@binkert.org        if 'module' in kwargs:
4613663Sandreas.sandberg@arm.com            raise AttributeError("module is an illegal attribute")
476501Snate@binkert.org
488452Snate@binkert.org        if 'output' in kwargs:
498452Snate@binkert.org            dir,tab = os.path.split(output)
508452Snate@binkert.org            if not tab.endswith('.py'):
5113663Sandreas.sandberg@arm.com                raise AttributeError('The output file must end with .py')
528452Snate@binkert.org            kwargs['outputdir'] = dir
538452Snate@binkert.org            kwargs['tabmodule'] = tab[:-3]
546501Snate@binkert.org
558452Snate@binkert.org        self.yacc_kwargs = kwargs
566501Snate@binkert.org
576660Snate@binkert.org    def __getattr__(self, attr):
588452Snate@binkert.org        if attr == 'lexers':
598452Snate@binkert.org            self.lexers = []
608452Snate@binkert.org            return self.lexers
616660Snate@binkert.org
628452Snate@binkert.org        if attr == 'lex_kwargs':
638452Snate@binkert.org            self.setupLexerFactory()
648452Snate@binkert.org            return self.lex_kwargs
656501Snate@binkert.org
668452Snate@binkert.org        if attr == 'yacc_kwargs':
678452Snate@binkert.org            self.setupParserFactory()
688452Snate@binkert.org            return self.yacc_kwargs
696501Snate@binkert.org
708452Snate@binkert.org        if attr == 'lex':
718452Snate@binkert.org            self.lex = ply.lex.lex(module=self, **self.lex_kwargs)
728452Snate@binkert.org            return self.lex
736501Snate@binkert.org
748452Snate@binkert.org        if attr == 'yacc':
758452Snate@binkert.org            self.yacc = ply.yacc.yacc(module=self, **self.yacc_kwargs)
768452Snate@binkert.org            return self.yacc
778452Snate@binkert.org
788452Snate@binkert.org        if attr == 'current_lexer':
798452Snate@binkert.org            if not self.lexers:
808452Snate@binkert.org                return None
818452Snate@binkert.org            return self.lexers[-1][0]
828452Snate@binkert.org
838452Snate@binkert.org        if attr == 'current_source':
848452Snate@binkert.org            if not self.lexers:
858452Snate@binkert.org                return '<none>'
868452Snate@binkert.org            return self.lexers[-1][1]
878452Snate@binkert.org
888452Snate@binkert.org        if attr == 'current_line':
898452Snate@binkert.org            if not self.lexers:
908452Snate@binkert.org                return -1
918452Snate@binkert.org            return self.current_lexer.lineno
928452Snate@binkert.org
9313663Sandreas.sandberg@arm.com        raise AttributeError(
9413663Sandreas.sandberg@arm.com            "'%s' object has no attribute '%s'" % (type(self), attr))
958452Snate@binkert.org
968452Snate@binkert.org    def parse_string(self, data, source='<string>', debug=None, tracking=0):
9713720Sandreas.sandberg@arm.com        if not isinstance(data, string_types):
9813663Sandreas.sandberg@arm.com            raise AttributeError(
9913663Sandreas.sandberg@arm.com                "argument must be a string, was '%s'" % type(f))
1008452Snate@binkert.org
1018452Snate@binkert.org        import new
1028452Snate@binkert.org        lexer = self.lex.clone()
1038452Snate@binkert.org        lexer.input(data)
1048452Snate@binkert.org        self.lexers.append((lexer, source))
1058452Snate@binkert.org        dict = {
1068452Snate@binkert.org            'productions' : self.yacc.productions,
1078452Snate@binkert.org            'action'      : self.yacc.action,
1088452Snate@binkert.org            'goto'        : self.yacc.goto,
1098452Snate@binkert.org            'errorfunc'   : self.yacc.errorfunc,
1108452Snate@binkert.org            }
1118452Snate@binkert.org        parser = new.instance(ply.yacc.LRParser, dict)
1128452Snate@binkert.org        result = parser.parse(lexer=lexer, debug=debug, tracking=tracking)
1138452Snate@binkert.org        self.lexers.pop()
1148452Snate@binkert.org        return result
1158452Snate@binkert.org
1168452Snate@binkert.org    def parse_file(self, f, **kwargs):
11713720Sandreas.sandberg@arm.com        if isinstance(f, string_types):
1188452Snate@binkert.org            source = f
11913670Sandreas.sandberg@arm.com            f = open(f, 'r')
1208452Snate@binkert.org        elif isinstance(f, file):
1218452Snate@binkert.org            source = f.name
1228452Snate@binkert.org        else:
12313663Sandreas.sandberg@arm.com            raise AttributeError(
12413663Sandreas.sandberg@arm.com                "argument must be either a string or file, was '%s'" % type(f))
1258452Snate@binkert.org
1268452Snate@binkert.org        return self.parse_string(f.read(), source, **kwargs)
1276501Snate@binkert.org
1286501Snate@binkert.org    def p_error(self, t):
1296501Snate@binkert.org        if t:
1308452Snate@binkert.org            msg = "Syntax error at %s:%d:%d\n>>%s<<" % \
1318452Snate@binkert.org                  (self.current_source, t.lineno, t.lexpos + 1, t.value)
1326501Snate@binkert.org        else:
1338452Snate@binkert.org            msg = "Syntax error at end of %s" % (self.current_source, )
1346501Snate@binkert.org        raise ParseError(msg, t)
1356501Snate@binkert.org
1368452Snate@binkert.org    def t_error(self, t):
1378452Snate@binkert.org        msg = "Illegal character %s @ %d:%d" % \
13813682Sandreas.sandberg@arm.com            (repr(t.value[0]), t.lineno, t.lexpos)
1398452Snate@binkert.org        raise ParseError(msg, t)
140