isa_parser: Turn the ISA Parser into a subclass of Grammar.
This is to prepare for future cleanup where we allow SCons to create a separate grammar class for each ISA
This commit is contained in:
parent
bae6a4a4d9
commit
baca1f0566
|
@ -34,8 +34,12 @@ import traceback
|
|||
# get type names
|
||||
from types import *
|
||||
|
||||
from ply import lex
|
||||
from ply import yacc
|
||||
from m5.util.grammar import Grammar
|
||||
|
||||
class ISAParser(Grammar):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ISAParser, self).__init__(*args, **kwargs)
|
||||
self.templateMap = {}
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
|
@ -117,13 +121,13 @@ reserved_map = { }
|
|||
for r in reserved:
|
||||
reserved_map[r.lower()] = r
|
||||
|
||||
def t_ID(t):
|
||||
def t_ID(self, t):
|
||||
r'[A-Za-z_]\w*'
|
||||
t.type = reserved_map.get(t.value,'ID')
|
||||
t.type = self.reserved_map.get(t.value, 'ID')
|
||||
return t
|
||||
|
||||
# Integer literal
|
||||
def t_INTLIT(t):
|
||||
def t_INTLIT(self, t):
|
||||
r'(0x[\da-fA-F]+)|\d+'
|
||||
try:
|
||||
t.value = int(t.value,0)
|
||||
|
@ -134,7 +138,7 @@ def t_INTLIT(t):
|
|||
|
||||
# String literal. Note that these use only single quotes, and
|
||||
# can span multiple lines.
|
||||
def t_STRLIT(t):
|
||||
def t_STRLIT(self, t):
|
||||
r"(?m)'([^'])+'"
|
||||
# strip off quotes
|
||||
t.value = t.value[1:-1]
|
||||
|
@ -144,24 +148,24 @@ def t_STRLIT(t):
|
|||
|
||||
# "Code literal"... like a string literal, but delimiters are
|
||||
# '{{' and '}}' so they get formatted nicely under emacs c-mode
|
||||
def t_CODELIT(t):
|
||||
def t_CODELIT(self, t):
|
||||
r"(?m)\{\{([^\}]|}(?!\}))+\}\}"
|
||||
# strip off {{ & }}
|
||||
t.value = t.value[2:-2]
|
||||
t.lexer.lineno += t.value.count('\n')
|
||||
return t
|
||||
|
||||
def t_CPPDIRECTIVE(t):
|
||||
def t_CPPDIRECTIVE(self, t):
|
||||
r'^\#[^\#].*\n'
|
||||
t.lexer.lineno += t.value.count('\n')
|
||||
return t
|
||||
|
||||
def t_NEWFILE(t):
|
||||
def t_NEWFILE(self, t):
|
||||
r'^\#\#newfile\s+"[\w/.-]*"'
|
||||
fileNameStack.push((t.value[11:-1], t.lexer.lineno))
|
||||
t.lexer.lineno = 0
|
||||
|
||||
def t_ENDFILE(t):
|
||||
def t_ENDFILE(self, t):
|
||||
r'^\#\#endfile'
|
||||
(old_filename, t.lexer.lineno) = fileNameStack.pop()
|
||||
|
||||
|
@ -171,48 +175,46 @@ def t_ENDFILE(t):
|
|||
#
|
||||
|
||||
# Newlines
|
||||
def t_NEWLINE(t):
|
||||
def t_NEWLINE(self, t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += t.value.count('\n')
|
||||
|
||||
# Comments
|
||||
def t_comment(t):
|
||||
def t_comment(self, t):
|
||||
r'//.*'
|
||||
|
||||
# Completely ignored characters
|
||||
t_ignore = ' \t\x0c'
|
||||
|
||||
# Error handler
|
||||
def t_error(t):
|
||||
def t_error(self, t):
|
||||
error(t.lexer.lineno, "illegal character '%s'" % t.value[0])
|
||||
t.skip(1)
|
||||
|
||||
# Build the lexer
|
||||
lexer = lex.lex()
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# Parser
|
||||
#
|
||||
# Every function whose name starts with 'p_' defines a grammar rule.
|
||||
# The rule is encoded in the function's doc string, while the
|
||||
# function body provides the action taken when the rule is matched.
|
||||
# The argument to each function is a list of the values of the
|
||||
# rule's symbols: t[0] for the LHS, and t[1..n] for the symbols
|
||||
# on the RHS. For tokens, the value is copied from the t.value
|
||||
# attribute provided by the lexer. For non-terminals, the value
|
||||
# is assigned by the producing rule; i.e., the job of the grammar
|
||||
# rule function is to set the value for the non-terminal on the LHS
|
||||
# (by assigning to t[0]).
|
||||
# Every function whose name starts with 'p_' defines a grammar
|
||||
# rule. The rule is encoded in the function's doc string, while
|
||||
# the function body provides the action taken when the rule is
|
||||
# matched. The argument to each function is a list of the values
|
||||
# of the rule's symbols: t[0] for the LHS, and t[1..n] for the
|
||||
# symbols on the RHS. For tokens, the value is copied from the
|
||||
# t.value attribute provided by the lexer. For non-terminals, the
|
||||
# value is assigned by the producing rule; i.e., the job of the
|
||||
# grammar rule function is to set the value for the non-terminal
|
||||
# on the LHS (by assigning to t[0]).
|
||||
#####################################################################
|
||||
|
||||
# The LHS of the first grammar rule is used as the start symbol
|
||||
# (in this case, 'specification'). Note that this rule enforces
|
||||
# that there will be exactly one namespace declaration, with 0 or more
|
||||
# global defs/decls before and after it. The defs & decls before
|
||||
# the namespace decl will be outside the namespace; those after
|
||||
# will be inside. The decoder function is always inside the namespace.
|
||||
def p_specification(t):
|
||||
# that there will be exactly one namespace declaration, with 0 or
|
||||
# more global defs/decls before and after it. The defs & decls
|
||||
# before the namespace decl will be outside the namespace; those
|
||||
# after will be inside. The decoder function is always inside the
|
||||
# namespace.
|
||||
def p_specification(self, t):
|
||||
'specification : opt_defs_and_outputs name_decl opt_defs_and_outputs decode_block'
|
||||
global_code = t[1]
|
||||
isa_name = t[2]
|
||||
|
@ -224,36 +226,37 @@ StaticInstPtr
|
|||
{
|
||||
using namespace %(namespace)s;
|
||||
''' % vars(), '}')
|
||||
# both the latter output blocks and the decode block are in the namespace
|
||||
# both the latter output blocks and the decode block are in
|
||||
# the namespace
|
||||
namespace_code = t[3] + t[4]
|
||||
# pass it all back to the caller of yacc.parse()
|
||||
t[0] = (isa_name, namespace, global_code, namespace_code)
|
||||
|
||||
# ISA name declaration looks like "namespace <foo>;"
|
||||
def p_name_decl(t):
|
||||
def p_name_decl(self, t):
|
||||
'name_decl : NAMESPACE ID SEMI'
|
||||
t[0] = t[2]
|
||||
|
||||
# 'opt_defs_and_outputs' is a possibly empty sequence of
|
||||
# def and/or output statements.
|
||||
def p_opt_defs_and_outputs_0(t):
|
||||
def p_opt_defs_and_outputs_0(self, t):
|
||||
'opt_defs_and_outputs : empty'
|
||||
t[0] = GenCode()
|
||||
|
||||
def p_opt_defs_and_outputs_1(t):
|
||||
def p_opt_defs_and_outputs_1(self, t):
|
||||
'opt_defs_and_outputs : defs_and_outputs'
|
||||
t[0] = t[1]
|
||||
|
||||
def p_defs_and_outputs_0(t):
|
||||
def p_defs_and_outputs_0(self, t):
|
||||
'defs_and_outputs : def_or_output'
|
||||
t[0] = t[1]
|
||||
|
||||
def p_defs_and_outputs_1(t):
|
||||
def p_defs_and_outputs_1(self, t):
|
||||
'defs_and_outputs : defs_and_outputs def_or_output'
|
||||
t[0] = t[1] + t[2]
|
||||
|
||||
# The list of possible definition/output statements.
|
||||
def p_def_or_output(t):
|
||||
def p_def_or_output(self, t):
|
||||
'''def_or_output : def_format
|
||||
| def_bitfield
|
||||
| def_bitfield_struct
|
||||
|
@ -269,40 +272,34 @@ def p_def_or_output(t):
|
|||
# Output blocks 'output <foo> {{...}}' (C++ code blocks) are copied
|
||||
# directly to the appropriate output section.
|
||||
|
||||
|
||||
# Protect any non-dict-substitution '%'s in a format string
|
||||
# (i.e. those not followed by '(')
|
||||
def protect_non_subst_percents(s):
|
||||
return re.sub(r'%(?!\()', '%%', s)
|
||||
|
||||
# Massage output block by substituting in template definitions and bit
|
||||
# operators. We handle '%'s embedded in the string that don't
|
||||
# indicate template substitutions (or CPU-specific symbols, which get
|
||||
# handled in GenCode) by doubling them first so that the format
|
||||
# operation will reduce them back to single '%'s.
|
||||
def process_output(s):
|
||||
# Massage output block by substituting in template definitions and
|
||||
# bit operators. We handle '%'s embedded in the string that don't
|
||||
# indicate template substitutions (or CPU-specific symbols, which
|
||||
# get handled in GenCode) by doubling them first so that the
|
||||
# format operation will reduce them back to single '%'s.
|
||||
def process_output(self, s):
|
||||
s = protect_non_subst_percents(s)
|
||||
# protects cpu-specific symbols too
|
||||
s = protect_cpu_symbols(s)
|
||||
return substBitOps(s % templateMap)
|
||||
return substBitOps(s % self.templateMap)
|
||||
|
||||
def p_output_header(t):
|
||||
def p_output_header(self, t):
|
||||
'output_header : OUTPUT HEADER CODELIT SEMI'
|
||||
t[0] = GenCode(header_output = process_output(t[3]))
|
||||
t[0] = GenCode(header_output = self.process_output(t[3]))
|
||||
|
||||
def p_output_decoder(t):
|
||||
def p_output_decoder(self, t):
|
||||
'output_decoder : OUTPUT DECODER CODELIT SEMI'
|
||||
t[0] = GenCode(decoder_output = process_output(t[3]))
|
||||
t[0] = GenCode(decoder_output = self.process_output(t[3]))
|
||||
|
||||
def p_output_exec(t):
|
||||
def p_output_exec(self, t):
|
||||
'output_exec : OUTPUT EXEC CODELIT SEMI'
|
||||
t[0] = GenCode(exec_output = process_output(t[3]))
|
||||
t[0] = GenCode(exec_output = self.process_output(t[3]))
|
||||
|
||||
# global let blocks 'let {{...}}' (Python code blocks) are executed
|
||||
# directly when seen. Note that these execute in a special variable
|
||||
# context 'exportContext' to prevent the code from polluting this
|
||||
# script's namespace.
|
||||
def p_global_let(t):
|
||||
# global let blocks 'let {{...}}' (Python code blocks) are
|
||||
# executed directly when seen. Note that these execute in a
|
||||
# special variable context 'exportContext' to prevent the code
|
||||
# from polluting this script's namespace.
|
||||
def p_global_let(self, t):
|
||||
'global_let : LET CODELIT SEMI'
|
||||
updateExportContext()
|
||||
exportContext["header_output"] = ''
|
||||
|
@ -319,9 +316,9 @@ def p_global_let(t):
|
|||
exec_output = exportContext["exec_output"],
|
||||
decode_block = exportContext["decode_block"])
|
||||
|
||||
# Define the mapping from operand type extensions to C++ types and bit
|
||||
# widths (stored in operandTypeMap).
|
||||
def p_def_operand_types(t):
|
||||
# Define the mapping from operand type extensions to C++ types and
|
||||
# bit widths (stored in operandTypeMap).
|
||||
def p_def_operand_types(self, t):
|
||||
'def_operand_types : DEF OPERAND_TYPES CODELIT SEMI'
|
||||
try:
|
||||
userDict = eval('{' + t[3] + '}')
|
||||
|
@ -331,9 +328,9 @@ def p_def_operand_types(t):
|
|||
buildOperandTypeMap(userDict, t.lexer.lineno)
|
||||
t[0] = GenCode() # contributes nothing to the output C++ file
|
||||
|
||||
# Define the mapping from operand names to operand classes and other
|
||||
# traits. Stored in operandNameMap.
|
||||
def p_def_operands(t):
|
||||
# Define the mapping from operand names to operand classes and
|
||||
# other traits. Stored in operandNameMap.
|
||||
def p_def_operands(self, t):
|
||||
'def_operands : DEF OPERANDS CODELIT SEMI'
|
||||
if not globals().has_key('operandTypeMap'):
|
||||
error(t.lexer.lineno,
|
||||
|
@ -349,7 +346,7 @@ def p_def_operands(t):
|
|||
# A bitfield definition looks like:
|
||||
# 'def [signed] bitfield <ID> [<first>:<last>]'
|
||||
# This generates a preprocessor macro in the output file.
|
||||
def p_def_bitfield_0(t):
|
||||
def p_def_bitfield_0(self, t):
|
||||
'def_bitfield : DEF opt_signed BITFIELD ID LESS INTLIT COLON INTLIT GREATER SEMI'
|
||||
expr = 'bits(machInst, %2d, %2d)' % (t[6], t[8])
|
||||
if (t[2] == 'signed'):
|
||||
|
@ -358,7 +355,7 @@ def p_def_bitfield_0(t):
|
|||
t[0] = GenCode(header_output = hash_define)
|
||||
|
||||
# alternate form for single bit: 'def [signed] bitfield <ID> [<bit>]'
|
||||
def p_def_bitfield_1(t):
|
||||
def p_def_bitfield_1(self, t):
|
||||
'def_bitfield : DEF opt_signed BITFIELD ID LESS INTLIT GREATER SEMI'
|
||||
expr = 'bits(machInst, %2d, %2d)' % (t[6], t[6])
|
||||
if (t[2] == 'signed'):
|
||||
|
@ -367,103 +364,101 @@ def p_def_bitfield_1(t):
|
|||
t[0] = GenCode(header_output = hash_define)
|
||||
|
||||
# alternate form for structure member: 'def bitfield <ID> <ID>'
|
||||
def p_def_bitfield_struct(t):
|
||||
def p_def_bitfield_struct(self, t):
|
||||
'def_bitfield_struct : DEF opt_signed BITFIELD ID id_with_dot SEMI'
|
||||
if (t[2] != ''):
|
||||
error(t.lexer.lineno, 'error: structure bitfields are always unsigned.')
|
||||
error(t.lexer.lineno,
|
||||
'error: structure bitfields are always unsigned.')
|
||||
expr = 'machInst.%s' % t[5]
|
||||
hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
|
||||
t[0] = GenCode(header_output = hash_define)
|
||||
|
||||
def p_id_with_dot_0(t):
|
||||
def p_id_with_dot_0(self, t):
|
||||
'id_with_dot : ID'
|
||||
t[0] = t[1]
|
||||
|
||||
def p_id_with_dot_1(t):
|
||||
def p_id_with_dot_1(self, t):
|
||||
'id_with_dot : ID DOT id_with_dot'
|
||||
t[0] = t[1] + t[2] + t[3]
|
||||
|
||||
def p_opt_signed_0(t):
|
||||
def p_opt_signed_0(self, t):
|
||||
'opt_signed : SIGNED'
|
||||
t[0] = t[1]
|
||||
|
||||
def p_opt_signed_1(t):
|
||||
def p_opt_signed_1(self, t):
|
||||
'opt_signed : empty'
|
||||
t[0] = ''
|
||||
|
||||
# Global map variable to hold templates
|
||||
templateMap = {}
|
||||
|
||||
def p_def_template(t):
|
||||
def p_def_template(self, t):
|
||||
'def_template : DEF TEMPLATE ID CODELIT SEMI'
|
||||
templateMap[t[3]] = Template(t[4])
|
||||
self.templateMap[t[3]] = Template(t[4])
|
||||
t[0] = GenCode()
|
||||
|
||||
# An instruction format definition looks like
|
||||
# "def format <fmt>(<params>) {{...}};"
|
||||
def p_def_format(t):
|
||||
def p_def_format(self, t):
|
||||
'def_format : DEF FORMAT ID LPAREN param_list RPAREN CODELIT SEMI'
|
||||
(id, params, code) = (t[3], t[5], t[7])
|
||||
defFormat(id, params, code, t.lexer.lineno)
|
||||
t[0] = GenCode()
|
||||
|
||||
# The formal parameter list for an instruction format is a possibly
|
||||
# empty list of comma-separated parameters. Positional (standard,
|
||||
# non-keyword) parameters must come first, followed by keyword
|
||||
# parameters, followed by a '*foo' parameter that gets excess
|
||||
# positional arguments (as in Python). Each of these three parameter
|
||||
# categories is optional.
|
||||
# The formal parameter list for an instruction format is a
|
||||
# possibly empty list of comma-separated parameters. Positional
|
||||
# (standard, non-keyword) parameters must come first, followed by
|
||||
# keyword parameters, followed by a '*foo' parameter that gets
|
||||
# excess positional arguments (as in Python). Each of these three
|
||||
# parameter categories is optional.
|
||||
#
|
||||
# Note that we do not support the '**foo' parameter for collecting
|
||||
# otherwise undefined keyword args. Otherwise the parameter list is
|
||||
# (I believe) identical to what is supported in Python.
|
||||
# otherwise undefined keyword args. Otherwise the parameter list
|
||||
# is (I believe) identical to what is supported in Python.
|
||||
#
|
||||
# The param list generates a tuple, where the first element is a list of
|
||||
# the positional params and the second element is a dict containing the
|
||||
# keyword params.
|
||||
def p_param_list_0(t):
|
||||
# The param list generates a tuple, where the first element is a
|
||||
# list of the positional params and the second element is a dict
|
||||
# containing the keyword params.
|
||||
def p_param_list_0(self, t):
|
||||
'param_list : positional_param_list COMMA nonpositional_param_list'
|
||||
t[0] = t[1] + t[3]
|
||||
|
||||
def p_param_list_1(t):
|
||||
def p_param_list_1(self, t):
|
||||
'''param_list : positional_param_list
|
||||
| nonpositional_param_list'''
|
||||
t[0] = t[1]
|
||||
|
||||
def p_positional_param_list_0(t):
|
||||
def p_positional_param_list_0(self, t):
|
||||
'positional_param_list : empty'
|
||||
t[0] = []
|
||||
|
||||
def p_positional_param_list_1(t):
|
||||
def p_positional_param_list_1(self, t):
|
||||
'positional_param_list : ID'
|
||||
t[0] = [t[1]]
|
||||
|
||||
def p_positional_param_list_2(t):
|
||||
def p_positional_param_list_2(self, t):
|
||||
'positional_param_list : positional_param_list COMMA ID'
|
||||
t[0] = t[1] + [t[3]]
|
||||
|
||||
def p_nonpositional_param_list_0(t):
|
||||
def p_nonpositional_param_list_0(self, t):
|
||||
'nonpositional_param_list : keyword_param_list COMMA excess_args_param'
|
||||
t[0] = t[1] + t[3]
|
||||
|
||||
def p_nonpositional_param_list_1(t):
|
||||
def p_nonpositional_param_list_1(self, t):
|
||||
'''nonpositional_param_list : keyword_param_list
|
||||
| excess_args_param'''
|
||||
t[0] = t[1]
|
||||
|
||||
def p_keyword_param_list_0(t):
|
||||
def p_keyword_param_list_0(self, t):
|
||||
'keyword_param_list : keyword_param'
|
||||
t[0] = [t[1]]
|
||||
|
||||
def p_keyword_param_list_1(t):
|
||||
def p_keyword_param_list_1(self, t):
|
||||
'keyword_param_list : keyword_param_list COMMA keyword_param'
|
||||
t[0] = t[1] + [t[3]]
|
||||
|
||||
def p_keyword_param(t):
|
||||
def p_keyword_param(self, t):
|
||||
'keyword_param : ID EQUALS expr'
|
||||
t[0] = t[1] + ' = ' + t[3].__repr__()
|
||||
|
||||
def p_excess_args_param(t):
|
||||
def p_excess_args_param(self, t):
|
||||
'excess_args_param : ASTERISK ID'
|
||||
# Just concatenate them: '*ID'. Wrap in list to be consistent
|
||||
# with positional_param_list and keyword_param_list.
|
||||
|
@ -476,7 +471,7 @@ def p_excess_args_param(t):
|
|||
# A decode block looks like:
|
||||
# decode <field1> [, <field2>]* [default <inst>] { ... }
|
||||
#
|
||||
def p_decode_block(t):
|
||||
def p_decode_block(self, t):
|
||||
'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE'
|
||||
default_defaults = defaultStack.pop()
|
||||
codeObj = t[5]
|
||||
|
@ -487,18 +482,19 @@ def p_decode_block(t):
|
|||
codeObj.wrap_decode_block('switch (%s) {\n' % t[2], '}\n')
|
||||
t[0] = codeObj
|
||||
|
||||
# The opt_default statement serves only to push the "default defaults"
|
||||
# onto defaultStack. This value will be used by nested decode blocks,
|
||||
# and used and popped off when the current decode_block is processed
|
||||
# (in p_decode_block() above).
|
||||
def p_opt_default_0(t):
|
||||
# The opt_default statement serves only to push the "default
|
||||
# defaults" onto defaultStack. This value will be used by nested
|
||||
# decode blocks, and used and popped off when the current
|
||||
# decode_block is processed (in p_decode_block() above).
|
||||
def p_opt_default_0(self, t):
|
||||
'opt_default : empty'
|
||||
# no default specified: reuse the one currently at the top of the stack
|
||||
# no default specified: reuse the one currently at the top of
|
||||
# the stack
|
||||
defaultStack.push(defaultStack.top())
|
||||
# no meaningful value returned
|
||||
t[0] = None
|
||||
|
||||
def p_opt_default_1(t):
|
||||
def p_opt_default_1(self, t):
|
||||
'opt_default : DEFAULT inst'
|
||||
# push the new default
|
||||
codeObj = t[2]
|
||||
|
@ -507,11 +503,11 @@ def p_opt_default_1(t):
|
|||
# no meaningful value returned
|
||||
t[0] = None
|
||||
|
||||
def p_decode_stmt_list_0(t):
|
||||
def p_decode_stmt_list_0(self, t):
|
||||
'decode_stmt_list : decode_stmt'
|
||||
t[0] = t[1]
|
||||
|
||||
def p_decode_stmt_list_1(t):
|
||||
def p_decode_stmt_list_1(self, t):
|
||||
'decode_stmt_list : decode_stmt decode_stmt_list'
|
||||
if (t[1].has_decode_default and t[2].has_decode_default):
|
||||
error(t.lexer.lineno, 'Two default cases in decode block')
|
||||
|
@ -527,43 +523,46 @@ def p_decode_stmt_list_1(t):
|
|||
# 4. C preprocessor directives.
|
||||
|
||||
|
||||
# Preprocessor directives found in a decode statement list are passed
|
||||
# through to the output, replicated to all of the output code
|
||||
# streams. This works well for ifdefs, so we can ifdef out both the
|
||||
# declarations and the decode cases generated by an instruction
|
||||
# definition. Handling them as part of the grammar makes it easy to
|
||||
# keep them in the right place with respect to the code generated by
|
||||
# the other statements.
|
||||
def p_decode_stmt_cpp(t):
|
||||
# Preprocessor directives found in a decode statement list are
|
||||
# passed through to the output, replicated to all of the output
|
||||
# code streams. This works well for ifdefs, so we can ifdef out
|
||||
# both the declarations and the decode cases generated by an
|
||||
# instruction definition. Handling them as part of the grammar
|
||||
# makes it easy to keep them in the right place with respect to
|
||||
# the code generated by the other statements.
|
||||
def p_decode_stmt_cpp(self, t):
|
||||
'decode_stmt : CPPDIRECTIVE'
|
||||
t[0] = GenCode(t[1], t[1], t[1], t[1])
|
||||
|
||||
# A format block 'format <foo> { ... }' sets the default instruction
|
||||
# format used to handle instruction definitions inside the block.
|
||||
# This format can be overridden by using an explicit format on the
|
||||
# instruction definition or with a nested format block.
|
||||
def p_decode_stmt_format(t):
|
||||
# A format block 'format <foo> { ... }' sets the default
|
||||
# instruction format used to handle instruction definitions inside
|
||||
# the block. This format can be overridden by using an explicit
|
||||
# format on the instruction definition or with a nested format
|
||||
# block.
|
||||
def p_decode_stmt_format(self, t):
|
||||
'decode_stmt : FORMAT push_format_id LBRACE decode_stmt_list RBRACE'
|
||||
# The format will be pushed on the stack when 'push_format_id' is
|
||||
# processed (see below). Once the parser has recognized the full
|
||||
# production (though the right brace), we're done with the format,
|
||||
# so now we can pop it.
|
||||
# The format will be pushed on the stack when 'push_format_id'
|
||||
# is processed (see below). Once the parser has recognized
|
||||
# the full production (though the right brace), we're done
|
||||
# with the format, so now we can pop it.
|
||||
formatStack.pop()
|
||||
t[0] = t[4]
|
||||
|
||||
# This rule exists so we can set the current format (& push the stack)
|
||||
# when we recognize the format name part of the format block.
|
||||
def p_push_format_id(t):
|
||||
# This rule exists so we can set the current format (& push the
|
||||
# stack) when we recognize the format name part of the format
|
||||
# block.
|
||||
def p_push_format_id(self, t):
|
||||
'push_format_id : ID'
|
||||
try:
|
||||
formatStack.push(formatMap[t[1]])
|
||||
t[0] = ('', '// format %s' % t[1])
|
||||
except KeyError:
|
||||
error(t.lexer.lineno, 'instruction format "%s" not defined.' % t[1])
|
||||
error(t.lexer.lineno,
|
||||
'instruction format "%s" not defined.' % t[1])
|
||||
|
||||
# Nested decode block: if the value of the current field matches the
|
||||
# specified constant, do a nested decode on some other field.
|
||||
def p_decode_stmt_decode(t):
|
||||
# Nested decode block: if the value of the current field matches
|
||||
# the specified constant, do a nested decode on some other field.
|
||||
def p_decode_stmt_decode(self, t):
|
||||
'decode_stmt : case_label COLON decode_block'
|
||||
label = t[1]
|
||||
codeObj = t[3]
|
||||
|
@ -574,7 +573,7 @@ def p_decode_stmt_decode(t):
|
|||
t[0] = codeObj
|
||||
|
||||
# Instruction definition (finally!).
|
||||
def p_decode_stmt_inst(t):
|
||||
def p_decode_stmt_inst(self, t):
|
||||
'decode_stmt : case_label COLON inst SEMI'
|
||||
label = t[1]
|
||||
codeObj = t[3]
|
||||
|
@ -582,32 +581,33 @@ def p_decode_stmt_inst(t):
|
|||
codeObj.has_decode_default = (label == 'default')
|
||||
t[0] = codeObj
|
||||
|
||||
# The case label is either a list of one or more constants or 'default'
|
||||
def p_case_label_0(t):
|
||||
# The case label is either a list of one or more constants or
|
||||
# 'default'
|
||||
def p_case_label_0(self, t):
|
||||
'case_label : intlit_list'
|
||||
t[0] = ': '.join(map(lambda a: 'case %#x' % a, t[1]))
|
||||
|
||||
def p_case_label_1(t):
|
||||
def p_case_label_1(self, t):
|
||||
'case_label : DEFAULT'
|
||||
t[0] = 'default'
|
||||
|
||||
#
|
||||
# The constant list for a decode case label must be non-empty, but may have
|
||||
# one or more comma-separated integer literals in it.
|
||||
# The constant list for a decode case label must be non-empty, but
|
||||
# may have one or more comma-separated integer literals in it.
|
||||
#
|
||||
def p_intlit_list_0(t):
|
||||
def p_intlit_list_0(self, t):
|
||||
'intlit_list : INTLIT'
|
||||
t[0] = [t[1]]
|
||||
|
||||
def p_intlit_list_1(t):
|
||||
def p_intlit_list_1(self, t):
|
||||
'intlit_list : intlit_list COMMA INTLIT'
|
||||
t[0] = t[1]
|
||||
t[0].append(t[3])
|
||||
|
||||
# Define an instruction using the current instruction format (specified
|
||||
# by an enclosing format block).
|
||||
# Define an instruction using the current instruction format
|
||||
# (specified by an enclosing format block).
|
||||
# "<mnemonic>(<args>)"
|
||||
def p_inst_0(t):
|
||||
def p_inst_0(self, t):
|
||||
'inst : ID LPAREN arg_list RPAREN'
|
||||
# Pass the ID and arg list to the current format class to deal with.
|
||||
currentFormat = formatStack.top()
|
||||
|
@ -621,110 +621,112 @@ def p_inst_0(t):
|
|||
|
||||
# Define an instruction using an explicitly specified format:
|
||||
# "<fmt>::<mnemonic>(<args>)"
|
||||
def p_inst_1(t):
|
||||
def p_inst_1(self, t):
|
||||
'inst : ID DBLCOLON ID LPAREN arg_list RPAREN'
|
||||
try:
|
||||
format = formatMap[t[1]]
|
||||
except KeyError:
|
||||
error(t.lexer.lineno, 'instruction format "%s" not defined.' % t[1])
|
||||
error(t.lexer.lineno,
|
||||
'instruction format "%s" not defined.' % t[1])
|
||||
codeObj = format.defineInst(t[3], t[5], t.lexer.lineno)
|
||||
comment = '\n// %s::%s(%s)\n' % (t[1], t[3], t[5])
|
||||
codeObj.prepend_all(comment)
|
||||
t[0] = codeObj
|
||||
|
||||
# The arg list generates a tuple, where the first element is a list of
|
||||
# the positional args and the second element is a dict containing the
|
||||
# keyword args.
|
||||
def p_arg_list_0(t):
|
||||
# The arg list generates a tuple, where the first element is a
|
||||
# list of the positional args and the second element is a dict
|
||||
# containing the keyword args.
|
||||
def p_arg_list_0(self, t):
|
||||
'arg_list : positional_arg_list COMMA keyword_arg_list'
|
||||
t[0] = ( t[1], t[3] )
|
||||
|
||||
def p_arg_list_1(t):
|
||||
def p_arg_list_1(self, t):
|
||||
'arg_list : positional_arg_list'
|
||||
t[0] = ( t[1], {} )
|
||||
|
||||
def p_arg_list_2(t):
|
||||
def p_arg_list_2(self, t):
|
||||
'arg_list : keyword_arg_list'
|
||||
t[0] = ( [], t[1] )
|
||||
|
||||
def p_positional_arg_list_0(t):
|
||||
def p_positional_arg_list_0(self, t):
|
||||
'positional_arg_list : empty'
|
||||
t[0] = []
|
||||
|
||||
def p_positional_arg_list_1(t):
|
||||
def p_positional_arg_list_1(self, t):
|
||||
'positional_arg_list : expr'
|
||||
t[0] = [t[1]]
|
||||
|
||||
def p_positional_arg_list_2(t):
|
||||
def p_positional_arg_list_2(self, t):
|
||||
'positional_arg_list : positional_arg_list COMMA expr'
|
||||
t[0] = t[1] + [t[3]]
|
||||
|
||||
def p_keyword_arg_list_0(t):
|
||||
def p_keyword_arg_list_0(self, t):
|
||||
'keyword_arg_list : keyword_arg'
|
||||
t[0] = t[1]
|
||||
|
||||
def p_keyword_arg_list_1(t):
|
||||
def p_keyword_arg_list_1(self, t):
|
||||
'keyword_arg_list : keyword_arg_list COMMA keyword_arg'
|
||||
t[0] = t[1]
|
||||
t[0].update(t[3])
|
||||
|
||||
def p_keyword_arg(t):
|
||||
def p_keyword_arg(self, t):
|
||||
'keyword_arg : ID EQUALS expr'
|
||||
t[0] = { t[1] : t[3] }
|
||||
|
||||
#
|
||||
# Basic expressions. These constitute the argument values of
|
||||
# "function calls" (i.e. instruction definitions in the decode block)
|
||||
# and default values for formal parameters of format functions.
|
||||
# "function calls" (i.e. instruction definitions in the decode
|
||||
# block) and default values for formal parameters of format
|
||||
# functions.
|
||||
#
|
||||
# Right now, these are either strings, integers, or (recursively)
|
||||
# lists of exprs (using Python square-bracket list syntax). Note that
|
||||
# bare identifiers are trated as string constants here (since there
|
||||
# isn't really a variable namespace to refer to).
|
||||
# lists of exprs (using Python square-bracket list syntax). Note
|
||||
# that bare identifiers are trated as string constants here (since
|
||||
# there isn't really a variable namespace to refer to).
|
||||
#
|
||||
def p_expr_0(t):
|
||||
def p_expr_0(self, t):
|
||||
'''expr : ID
|
||||
| INTLIT
|
||||
| STRLIT
|
||||
| CODELIT'''
|
||||
t[0] = t[1]
|
||||
|
||||
def p_expr_1(t):
|
||||
def p_expr_1(self, t):
|
||||
'''expr : LBRACKET list_expr RBRACKET'''
|
||||
t[0] = t[2]
|
||||
|
||||
def p_list_expr_0(t):
|
||||
def p_list_expr_0(self, t):
|
||||
'list_expr : expr'
|
||||
t[0] = [t[1]]
|
||||
|
||||
def p_list_expr_1(t):
|
||||
def p_list_expr_1(self, t):
|
||||
'list_expr : list_expr COMMA expr'
|
||||
t[0] = t[1] + [t[3]]
|
||||
|
||||
def p_list_expr_2(t):
|
||||
def p_list_expr_2(self, t):
|
||||
'list_expr : empty'
|
||||
t[0] = []
|
||||
|
||||
#
|
||||
# Empty production... use in other rules for readability.
|
||||
#
|
||||
def p_empty(t):
|
||||
def p_empty(self, t):
|
||||
'empty :'
|
||||
pass
|
||||
|
||||
# Parse error handler. Note that the argument here is the offending
|
||||
# *token*, not a grammar symbol (hence the need to use t.value)
|
||||
def p_error(t):
|
||||
# Parse error handler. Note that the argument here is the
|
||||
# offending *token*, not a grammar symbol (hence the need to use
|
||||
# t.value)
|
||||
def p_error(self, t):
|
||||
if t:
|
||||
error(t.lexer.lineno, "syntax error at '%s'" % t.value)
|
||||
else:
|
||||
error(0, "unknown syntax error", True)
|
||||
|
||||
# END OF GRAMMAR RULES
|
||||
#
|
||||
# Now build the parser.
|
||||
parser = yacc.yacc()
|
||||
|
||||
# Now build the parser.
|
||||
parser = ISAParser()
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
|
@ -761,6 +763,11 @@ def expand_cpu_symbols_to_string(template):
|
|||
def protect_cpu_symbols(template):
|
||||
return re.sub(r'%(?=\(CPU_)', '%%', template)
|
||||
|
||||
# Protect any non-dict-substitution '%'s in a format string
|
||||
# (i.e. those not followed by '(')
|
||||
def protect_non_subst_percents(s):
|
||||
return re.sub(r'%(?!\()', '%%', s)
|
||||
|
||||
###############
|
||||
# GenCode class
|
||||
#
|
||||
|
@ -834,7 +841,7 @@ exportContext = {}
|
|||
|
||||
def updateExportContext():
|
||||
exportContext.update(exportDict(*exportContextSymbols))
|
||||
exportContext.update(templateMap)
|
||||
exportContext.update(parser.templateMap)
|
||||
|
||||
def exportDict(*symNames):
|
||||
return dict([(s, eval(s)) for s in symNames])
|
||||
|
@ -1044,7 +1051,7 @@ class Template:
|
|||
# Build a dict ('myDict') to use for the template substitution.
|
||||
# Start with the template namespace. Make a copy since we're
|
||||
# going to modify it.
|
||||
myDict = templateMap.copy()
|
||||
myDict = parser.templateMap.copy()
|
||||
|
||||
if isinstance(d, InstObjParams):
|
||||
# If we're dealing with an InstObjParams object, we need
|
||||
|
@ -1970,8 +1977,7 @@ def parse_isa_desc(isa_desc_file, output_dir):
|
|||
fileNameStack.push((isa_desc_file, 0))
|
||||
|
||||
# Parse it.
|
||||
(isa_name, namespace, global_code, namespace_code) = \
|
||||
parser.parse(isa_desc, lexer=lexer)
|
||||
(isa_name, namespace, global_code, namespace_code) = parser.parse(isa_desc)
|
||||
|
||||
# grab the last three path components of isa_desc_file to put in
|
||||
# the output
|
||||
|
|
Loading…
Reference in a new issue