# Copyright (c) 2009 The Hewlett-Packard Development Company # 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. # # Authors: Nathan Binkert import os.path import re import sys from m5.util import code_formatter from m5.util.grammar import Grammar, TokenError, ParseError import slicc.ast as ast import slicc.util as util from slicc.symbols import SymbolTable def read_slicc(sources): if not isinstance(sources, (list,tuple)): sources = [ sources ] for source in sources: for sm_file in file(source, "r"): sm_file = sm_file.strip() if not sm_file: continue if sm_file.startswith("#"): continue yield sm_file class SLICC(Grammar): def __init__(self, protocol, **kwargs): super(SLICC, self).__init__(**kwargs) self.decl_list_vec = [] self.current_file = None self.protocol = protocol self.symtab = SymbolTable(self) def codeFormatter(self, *args, **kwargs): code = code_formatter(*args, **kwargs) code['protocol'] = self.protocol return code def parse(self, filename): self.current_file = filename f = file(filename, 'r') text = f.read() try: decl_list = super(SLICC, self).parse(text) except (TokenError, ParseError), e: sys.exit("%s: %s:%d" % (e, filename, e.token.lineno)) self.decl_list_vec.append(decl_list) self.current_file = None def _load(self, *filenames): filenames = list(filenames) while filenames: f = filenames.pop(0) if isinstance(f, (list, tuple)): filenames[0:0] = list(f) continue yield f if f.endswith(".slicc"): dirname,basename = os.path.split(f) filenames[0:0] = [ os.path.join(dirname, x) \ for x in read_slicc(f)] else: assert f.endswith(".sm") self.parse(f) def load(self, *filenames, **kwargs): verbose = kwargs.pop("verbose", False) if kwargs: raise TypeError gen = self._load(*filenames) if verbose: return gen else: # Run out the generator if we don't want the verbosity for foo in gen: pass def findMachines(self): for decl_list in self.decl_list_vec: decl_list.findMachines() def generate(self): for decl_list in self.decl_list_vec: decl_list.generate() def writeCodeFiles(self, code_path): util.makeDir(code_path) self.symtab.writeCodeFiles(code_path) def writeHTMLFiles(self, code_path): util.makeDir(code_path) self.symtab.writeHTMLFiles(code_path) def files(self): f = set([ 'MachineType.cc', 'MachineType.hh', 'Types.hh' ]) for decl_list in self.decl_list_vec: f |= decl_list.files() return f t_ignore = '\t ' # C or C++ comment (ignore) def t_c_comment(self, t): r'/\*(.|\n)*?\*/' t.lexer.lineno += t.value.count('\n') def t_cpp_comment(self, t): r'//.*' # Define a rule so we can track line numbers def t_newline(self, t): r'\n+' t.lexer.lineno += len(t.value) reserved = { 'global' : 'GLOBAL', 'machine' : 'MACHINE', 'in_port' : 'IN_PORT', 'out_port' : 'OUT_PORT', 'action' : 'ACTION', 'transition' : 'TRANS', 'structure' : 'STRUCT', 'external_type' : 'EXTERN_TYPE', 'enumeration' : 'ENUM', 'state_declaration' : 'STATE_DECL', 'peek' : 'PEEK', 'stall_and_wait' : 'STALL_AND_WAIT', 'wake_up_dependents' : 'WAKE_UP_DEPENDENTS', 'enqueue' : 'ENQUEUE', 'copy_head' : 'COPY_HEAD', 'check_allocate' : 'CHECK_ALLOCATE', 'check_stop_slots' : 'CHECK_STOP_SLOTS', 'static_cast' : 'STATIC_CAST', 'if' : 'IF', 'is_valid' : 'IS_VALID', 'is_invalid' : 'IS_INVALID', 'else' : 'ELSE', 'return' : 'RETURN', 'THIS' : 'THIS', 'CHIP' : 'CHIP', 'void' : 'VOID', 'new' : 'NEW', 'OOD' : 'OOD', } literals = ':[]{}(),=' tokens = [ 'EQ', 'NE', 'LT', 'GT', 'LE', 'GE', 'LEFTSHIFT', 'RIGHTSHIFT', 'NOT', 'AND', 'OR', 'PLUS', 'DASH', 'STAR', 'SLASH', 'DOUBLE_COLON', 'SEMI', 'ASSIGN', 'DOT', 'IDENT', 'LIT_BOOL', 'FLOATNUMBER', 'NUMBER', 'STRING' ] tokens += reserved.values() t_EQ = r'==' t_NE = r'!=' t_LT = r'<' t_GT = r'>' t_LE = r'<=' t_GE = r'>=' t_LEFTSHIFT = r'<<' t_RIGHTSHIFT = r'>>' t_NOT = r'!' t_AND = r'&&' t_OR = r'\|\|' t_PLUS = r'\+' t_DASH = r'-' t_STAR = r'\*' t_SLASH = r'/' t_DOUBLE_COLON = r'::' t_SEMI = r';' t_ASSIGN = r':=' t_DOT = r'\.' precedence = ( ('left', 'AND', 'OR'), ('left', 'EQ', 'NE'), ('left', 'LT', 'GT', 'LE', 'GE'), ('left', 'RIGHTSHIFT', 'LEFTSHIFT'), ('left', 'PLUS', 'DASH'), ('left', 'STAR', 'SLASH'), ('right', 'NOT', 'UMINUS'), ) def t_IDENT(self, t): r'[a-zA-Z_][a-zA-Z_0-9]*' if t.value == 'true': t.type = 'LIT_BOOL' t.value = True return t if t.value == 'false': t.type = 'LIT_BOOL' t.value = False return t # Check for reserved words t.type = self.reserved.get(t.value, 'IDENT') return t def t_FLOATNUMBER(self, t): '[0-9]+[.][0-9]+' try: t.value = float(t.value) except ValueError: raise TokenError("Illegal float", t) return t def t_NUMBER(self, t): r'[0-9]+' try: t.value = int(t.value) except ValueError: raise TokenError("Illegal number", t) return t def t_STRING1(self, t): r'\"[^"\n]*\"' t.type = 'STRING' t.value = t.value[1:-1] return t def t_STRING2(self, t): r"\'[^'\n]*\'" t.type = 'STRING' t.value = t.value[1:-1] return t def p_file(self, p): "file : decls" p[0] = p[1] def p_empty(self, p): "empty :" def p_decls(self, p): "decls : declsx" p[0] = ast.DeclListAST(self, p[1]) def p_declsx__list(self, p): "declsx : decl declsx" p[0] = [ p[1] ] + p[2] def p_declsx__none(self, p): "declsx : empty" p[0] = [] def p_decl__machine(self, p): "decl : MACHINE '(' ident pairs ')' ':' params '{' decls '}'" p[0] = ast.MachineAST(self, p[3], p[4], p[7], p[9]) def p_decl__action(self, p): "decl : ACTION '(' ident pairs ')' statements" p[0] = ast.ActionDeclAST(self, p[3], p[4], p[6]) def p_decl__in_port(self, p): "decl : IN_PORT '(' ident ',' type ',' var pairs ')' statements" p[0] = ast.InPortDeclAST(self, p[3], p[5], p[7], p[8], p[10]) def p_decl__out_port(self, p): "decl : OUT_PORT '(' ident ',' type ',' var pairs ')' SEMI" p[0] = ast.OutPortDeclAST(self, p[3], p[5], p[7], p[8]) def p_decl__trans0(self, p): "decl : TRANS '(' idents ',' idents ',' ident pairs ')' idents" p[0] = ast.TransitionDeclAST(self, p[3], p[5], p[7], p[8], p[10]) def p_decl__trans1(self, p): "decl : TRANS '(' idents ',' idents pairs ')' idents" p[0] = ast.TransitionDeclAST(self, p[3], p[5], None, p[6], p[8]) def p_decl__extern0(self, p): "decl : EXTERN_TYPE '(' type pairs ')' SEMI" p[4]["external"] = "yes" p[0] = ast.TypeDeclAST(self, p[3], p[4], []) def p_decl__extern1(self, p): "decl : EXTERN_TYPE '(' type pairs ')' '{' type_methods '}'" p[4]["external"] = "yes" p[0] = ast.TypeDeclAST(self, p[3], p[4], p[7]) def p_decl__global(self, p): "decl : GLOBAL '(' type pairs ')' '{' type_members '}'" p[4]["global"] = "yes" p[0] = ast.TypeDeclAST(self, p[3], p[4], p[7]) def p_decl__struct(self, p): "decl : STRUCT '(' type pairs ')' '{' type_members '}'" p[0] = ast.TypeDeclAST(self, p[3], p[4], p[7]) def p_decl__enum(self, p): "decl : ENUM '(' type pairs ')' '{' type_enums '}'" p[4]["enumeration"] = "yes" p[0] = ast.EnumDeclAST(self, p[3], p[4], p[7]) def p_decl__state_decl(self, p): "decl : STATE_DECL '(' type pairs ')' '{' type_states '}'" p[4]["enumeration"] = "yes" p[4]["state_decl"] = "yes" p[0] = ast.StateDeclAST(self, p[3], p[4], p[7]) def p_decl__object(self, p): "decl : type ident pairs SEMI" p[0] = ast.ObjDeclAST(self, p[1], p[2], p[3]) def p_decl__func_decl(self, p): """decl : void ident '(' params ')' pairs SEMI | type ident '(' params ')' pairs SEMI""" p[0] = ast.FuncDeclAST(self, p[1], p[2], p[4], p[6], None) def p_decl__func_def(self, p): """decl : void ident '(' params ')' pairs statements | type ident '(' params ')' pairs statements""" p[0] = ast.FuncDeclAST(self, p[1], p[2], p[4], p[6], p[7]) # Type fields def p_type_members__list(self, p): "type_members : type_member type_members" p[0] = [ p[1] ] + p[2] def p_type_members__empty(self, p): "type_members : empty" p[0] = [] def p_type_member__1(self, p): "type_member : type ident pairs SEMI" p[0] = ast.TypeFieldMemberAST(self, p[1], p[2], p[3], None) def p_type_member__2(self, p): "type_member : type ident ASSIGN expr SEMI" p[0] = ast.TypeFieldMemberAST(self, p[1], p[2], ast.PairListAST(self), p[4]) # Methods def p_type_methods__list(self, p): "type_methods : type_method type_methods" p[0] = [ p[1] ] + p[2] def p_type_methods(self, p): "type_methods : empty" p[0] = [] def p_type_method(self, p): "type_method : type_or_void ident '(' types ')' pairs SEMI" p[0] = ast.TypeFieldMethodAST(self, p[1], p[2], p[4], p[6]) # Enum fields def p_type_enums__list(self, p): "type_enums : type_enum type_enums" p[0] = [ p[1] ] + p[2] def p_type_enums__empty(self, p): "type_enums : empty" p[0] = [] def p_type_enum(self, p): "type_enum : ident pairs SEMI" p[0] = ast.TypeFieldEnumAST(self, p[1], p[2]) # States def p_type_states__list(self, p): "type_states : type_state type_states" p[0] = [ p[1] ] + p[2] def p_type_states__empty(self, p): "type_states : empty" p[0] = [] def p_type_state(self, p): "type_state : ident ',' enumeration pairs SEMI" p[0] = ast.TypeFieldStateAST(self, p[1], p[3], p[4]) # Type def p_types__multiple(self, p): "types : type ',' types" p[0] = [ p[1] ] + p[3] def p_types__one(self, p): "types : type" p[0] = [ p[1] ] def p_types__empty(self, p): "types : empty" p[0] = [] def p_typestr__multi(self, p): "typestr : typestr DOUBLE_COLON ident" p[0] = '%s::%s' % (p[1], p[3]) def p_typestr__single(self, p): "typestr : ident" p[0] = p[1] def p_type__one(self, p): "type : typestr" p[0] = ast.TypeAST(self, p[1]) def p_void(self, p): "void : VOID" p[0] = ast.TypeAST(self, p[1]) def p_type_or_void(self, p): """type_or_void : type | void""" p[0] = p[1] # Formal Param def p_params__many(self, p): "params : param ',' params" p[0] = [ p[1] ] + p[3] def p_params__one(self, p): "params : param" p[0] = [ p[1] ] def p_params__none(self, p): "params : empty" p[0] = [] def p_param(self, p): "param : type ident" p[0] = ast.FormalParamAST(self, p[1], p[2]) def p_param__pointer(self, p): "param : type STAR ident" p[0] = ast.FormalParamAST(self, p[1], p[3], None, True) def p_param__pointer_default(self, p): "param : type STAR ident '=' STRING" p[0] = ast.FormalParamAST(self, p[1], p[3], p[5], True) def p_param__default_number(self, p): "param : type ident '=' NUMBER" p[0] = ast.FormalParamAST(self, p[1], p[2], p[4]) def p_param__default_bool(self, p): "param : type ident '=' LIT_BOOL" p[0] = ast.FormalParamAST(self, p[1], p[2], p[4]) def p_param__default_string(self, p): "param : type ident '=' STRING" p[0] = ast.FormalParamAST(self, p[1], p[2], p[4]) # Idents and lists def p_idents__braced(self, p): "idents : '{' identx '}'" p[0] = p[2] def p_idents__bare(self, p): "idents : ident" p[0] = [ p[1] ] def p_identx__multiple_1(self, p): """identx : ident SEMI identx | ident ',' identx""" p[0] = [ p[1] ] + p[3] def p_identx__multiple_2(self, p): "identx : ident identx" p[0] = [ p[1] ] + p[2] def p_identx__single(self, p): "identx : empty" p[0] = [ ] def p_ident(self, p): "ident : IDENT" p[0] = p[1] # Pair and pair lists def p_pairs__list(self, p): "pairs : ',' pairsx" p[0] = p[2] def p_pairs__empty(self, p): "pairs : empty" p[0] = ast.PairListAST(self) def p_pairsx__many(self, p): "pairsx : pair ',' pairsx" p[0] = p[3] p[0].addPair(p[1]) def p_pairsx__one(self, p): "pairsx : pair" p[0] = ast.PairListAST(self) p[0].addPair(p[1]) def p_pair__assign(self, p): """pair : ident '=' STRING | ident '=' ident | ident '=' NUMBER""" p[0] = ast.PairAST(self, p[1], p[3]) def p_pair__literal(self, p): "pair : STRING" p[0] = ast.PairAST(self, "short", p[1]) # Below are the rules for action descriptions def p_statements__inner(self, p): "statements : '{' statements_inner '}'" p[0] = ast.StatementListAST(self, p[2]) def p_statements__none(self, p): "statements : '{' '}'" p[0] = ast.StatementListAST(self, []) def p_statements_inner__many(self, p): "statements_inner : statement statements_inner" p[0] = [ p[1] ] + p[2] def p_statements_inner__one(self, p): "statements_inner : statement" p[0] = [ p[1] ] def p_exprs__multiple(self, p): "exprs : expr ',' exprs" p[0] = [ p[1] ] + p[3] def p_exprs__one(self, p): "exprs : expr" p[0] = [ p[1] ] def p_exprs__empty(self, p): "exprs : empty""" p[0] = [] def p_statement__expression(self, p): "statement : expr SEMI" p[0] = ast.ExprStatementAST(self, p[1]) def p_statement__assign(self, p): "statement : expr ASSIGN expr SEMI" p[0] = ast.AssignStatementAST(self, p[1], p[3]) def p_statement__enqueue(self, p): "statement : ENQUEUE '(' var ',' type pairs ')' statements" p[0] = ast.EnqueueStatementAST(self, p[3], p[5], p[6], p[8]) def p_statement__stall_and_wait(self, p): "statement : STALL_AND_WAIT '(' var ',' var ')' SEMI" p[0] = ast.StallAndWaitStatementAST(self, p[3], p[5]) def p_statement__wake_up_dependents(self, p): "statement : WAKE_UP_DEPENDENTS '(' var ')' SEMI" p[0] = ast.WakeUpDependentsStatementAST(self, p[3]) def p_statement__peek(self, p): "statement : PEEK '(' var ',' type pairs ')' statements" p[0] = ast.PeekStatementAST(self, p[3], p[5], p[6], p[8], "peek") def p_statement__copy_head(self, p): "statement : COPY_HEAD '(' var ',' var pairs ')' SEMI" p[0] = ast.CopyHeadStatementAST(self, p[3], p[5], p[6]) def p_statement__check_allocate(self, p): "statement : CHECK_ALLOCATE '(' var ')' SEMI" p[0] = ast.CheckAllocateStatementAST(self, p[3]) def p_statement__check_stop(self, p): "statement : CHECK_STOP_SLOTS '(' var ',' STRING ',' STRING ')' SEMI" p[0] = ast.CheckStopStatementAST(self, p[3], p[5], p[7]) def p_statement__static_cast(self, p): "aexpr : STATIC_CAST '(' type ',' expr ')'" p[0] = ast.StaticCastAST(self, p[3], "ref", p[5]) def p_statement__static_cast_ptr(self, p): "aexpr : STATIC_CAST '(' type ',' STRING ',' expr ')'" p[0] = ast.StaticCastAST(self, p[3], p[5], p[7]) def p_statement__return(self, p): "statement : RETURN expr SEMI" p[0] = ast.ReturnStatementAST(self, p[2]) def p_statement__if(self, p): "statement : if_statement" p[0] = p[1] def p_if_statement__if(self, p): "if_statement : IF '(' expr ')' statements" p[0] = ast.IfStatementAST(self, p[3], p[5], None) def p_if_statement__if_else(self, p): "if_statement : IF '(' expr ')' statements ELSE statements" p[0] = ast.IfStatementAST(self, p[3], p[5], p[7]) def p_statement__if_else_if(self, p): "if_statement : IF '(' expr ')' statements ELSE if_statement" p[0] = ast.IfStatementAST(self, p[3], p[5], ast.StatementListAST(self, p[7])) def p_expr__var(self, p): "aexpr : var" p[0] = p[1] def p_expr__localvar(self, p): "aexpr : type ident" p[0] = ast.LocalVariableAST(self, p[1], p[2]) def p_expr__literal(self, p): "aexpr : literal" p[0] = p[1] def p_expr__enumeration(self, p): "aexpr : enumeration" p[0] = p[1] def p_expr__func_call(self, p): "aexpr : ident '(' exprs ')'" p[0] = ast.FuncCallExprAST(self, p[1], p[3]) def p_expr__new(self, p): "aexpr : NEW type" p[0] = ast.NewExprAST(self, p[2]) def p_expr__null(self, p): "aexpr : OOD" p[0] = ast.OodAST(self) # globally access a local chip component and call a method def p_expr__local_chip_method(self, p): "aexpr : THIS DOT var '[' expr ']' DOT var DOT ident '(' exprs ')'" p[0] = ast.LocalChipMethodAST(self, p[3], p[5], p[8], p[10], p[12]) # globally access a local chip component and access a data member def p_expr__local_chip_member(self, p): "aexpr : THIS DOT var '[' expr ']' DOT var DOT field" p[0] = ast.LocalChipMemberAST(self, p[3], p[5], p[8], p[10]) # globally access a specified chip component and call a method def p_expr__specified_chip_method(self, p): "aexpr : CHIP '[' expr ']' DOT var '[' expr ']' DOT var DOT ident '(' exprs ')'" p[0] = ast.SpecifiedChipMethodAST(self, p[3], p[6], p[8], p[11], p[13], p[15]) # globally access a specified chip component and access a data member def p_expr__specified_chip_member(self, p): "aexpr : CHIP '[' expr ']' DOT var '[' expr ']' DOT var DOT field" p[0] = ast.SpecifiedChipMemberAST(self, p[3], p[6], p[8], p[11], p[13]) def p_expr__member(self, p): "aexpr : aexpr DOT ident" p[0] = ast.MemberExprAST(self, p[1], p[3]) def p_expr__member_method_call(self, p): "aexpr : aexpr DOT ident '(' exprs ')'" p[0] = ast.MemberMethodCallExprAST(self, p[1], p[3], p[5]) def p_expr__member_method_call_lookup(self, p): "aexpr : aexpr '[' exprs ']'" p[0] = ast.MemberMethodCallExprAST(self, p[1], "lookup", p[3]) def p_expr__class_method_call(self, p): "aexpr : type DOUBLE_COLON ident '(' exprs ')'" p[0] = ast.ClassMethodCallExprAST(self, p[1], p[3], p[5]) def p_expr__aexpr(self, p): "expr : aexpr" p[0] = p[1] def p_expr__binary_op(self, p): """expr : expr STAR expr | expr SLASH expr | expr PLUS expr | expr DASH expr | expr LT expr | expr GT expr | expr LE expr | expr GE expr | expr EQ expr | expr NE expr | expr AND expr | expr OR expr | expr RIGHTSHIFT expr | expr LEFTSHIFT expr""" p[0] = ast.InfixOperatorExprAST(self, p[1], p[2], p[3]) # FIXME - unary not def p_expr__unary_op(self, p): """expr : NOT expr | DASH expr %prec UMINUS""" p[0] = PrefixOperatorExpr(p[1], p[2]) def p_expr__parens(self, p): "aexpr : '(' expr ')'" p[0] = p[2] def p_expr__is_valid_ptr(self, p): "aexpr : IS_VALID '(' var ')'" p[0] = ast.IsValidPtrExprAST(self, p[3], True) def p_expr__is_invalid_ptr(self, p): "aexpr : IS_INVALID '(' var ')'" p[0] = ast.IsValidPtrExprAST(self, p[3], False) def p_literal__string(self, p): "literal : STRING" p[0] = ast.LiteralExprAST(self, p[1], "std::string") def p_literal__number(self, p): "literal : NUMBER" p[0] = ast.LiteralExprAST(self, p[1], "int") def p_literal__float(self, p): "literal : FLOATNUMBER" p[0] = ast.LiteralExprAST(self, p[1], "int") def p_literal__bool(self, p): "literal : LIT_BOOL" p[0] = ast.LiteralExprAST(self, p[1], "bool") def p_enumeration(self, p): "enumeration : ident ':' ident" p[0] = ast.EnumExprAST(self, ast.TypeAST(self, p[1]), p[3]) def p_var(self, p): "var : ident" p[0] = ast.VarExprAST(self, p[1]) def p_field(self, p): "field : ident" p[0] = p[1]