'COMMENT', 'DOTRANGE')
tokens = reserved + (
- 'SEMI', 'COMMA',
- 'NAME','INTNUM', 'DOUBLENUM', 'BOOLNUM', 'STRING', 'FUNCALL',
+ 'EOS', 'COMMA',
+ 'NAME','ARRAYNAME','INTNUM', 'DOUBLENUM', 'BOOLNUM', 'STRING', 'FUNCALL',
'LPAREN','RPAREN',
'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
'NOT', 'OR', 'AND', 'THEN', 'EQV',
# Tokens
-t_SEMI = r';'
+t_EOS = r'eos'
t_COMMA = r'\,'
t_ABS = r'\.ABS\.'
t_POW = r'\.P\.'
for r in reserved:
reserved_map[r] = r
+def t_ARRAYNAME(t):
+ r'%[A-Z][A-Z0-9]*'
+ t.value = t.value.lower()[1:]
+ return t
+
def t_NAME(t):
r'[A-Z][A-Z0-9_]*(\.(?!P))?'
t.type = reserved_map.get(t.value, 'NAME')
| program card'''
def p_card(t):
- 'card : statement SEMI'
+ 'card : statement EOS'
deck.append((sourcefile, t.lineno(1), None, t[1]))
def p_labeled_card(t):
- 'card : NAME statement SEMI'
+ 'card : NAME statement EOS'
deck.append((sourcefile, t.lineno(1), t[1], t[2]))
# Statement level
'statement : DIMENSION dimension_list'
t[0] = ('dimension', t[2])
def p_dimension_list(t):
- 'dimension_list : NAME application'
+ 'dimension_list : ARRAYNAME application'
t[0] = [[t[1], tuple(t[2])]]
def p_dimension_list_continue(t):
- 'dimension_list : dimension_list COMMA NAME application'
+ 'dimension_list : dimension_list COMMA ARRAYNAME application'
t[0] = t[1] + [[t[3], tuple(t[4])]]
## END
## PRINT_FORMAT
def p_print_format(t):
- 'statement : PRINT_FORMAT NAME COMMA outlist'
- t[0] = ('print', t[2]) + tuple(t[4])
-def p_print_format0(t):
- 'statement : PRINT_FORMAT NAME'
- t[0] = ('print', t[2])
+ 'statement : PRINT_FORMAT outlist'
+ t[0] = ('print',) + tuple(t[2])
# This is shared with SAVE DATA
-def p_outlist(t):
- 'outlist : outval'
- t[0] = [t[1]]
def p_outlist_continue(t):
'outlist : outlist COMMA outval'
t[0] = t[1] + [t[3]]
+def p_outlist(t):
+ 'outlist : outval'
+ t[0] = [t[1]]
def p_outval(t):
'''outval : range
| expression'''
t[0] = t[1]
def p_range(t):
- 'range : NAME application DOTRANGE NAME application'
- t[0] = ('slice', t[1], t[2], t[5])
+ 'range : ARRAYNAME application DOTRANGE ARRAYNAME application'
+ t[0] = ('range', t[1], t[2], t[5])
if t[1] != t[4]:
sys.stderr.write("variable names in range don't match!")
sys.exit(1)
## READ_FORMAT
def p_read_format(t):
- 'statement : READ_FORMAT NAME COMMA varlist'
- t[0] = ('read', t[2]) + tuple(t[4])
+ 'statement : READ_FORMAT varlist'
+ t[0] = ('read',) + tuple(t[2])
# This is shared with RESTORE_DATA
-def p_varlist(t):
- 'varlist : NAME'
- t[0] = [t[1]]
-def p_varlist_range(t):
- 'varlist : varlist COMMA range'
- t[0] = t[1] + [t[3]]
def p_varlist_continue(t):
- 'varlist : varlist COMMA NAME'
+ 'varlist : varlist COMMA lval'
t[0] = t[1] + [t[3]]
+def p_varlist(t):
+ 'varlist : lval'
+ t[0] = [t[1]]
+def p_lval(t):
+ '''lval : range
+ | NAME'''
+ t[0] = t[1]
## RESTORE
def p_restore_data(t):
t[0] = ('doublenum', t[1])
def p_expression_subscript(t):
- 'expression : NAME application'
+ 'expression : ARRAYNAME application'
t[0] = ('variable', t[1], tuple(t[2]))
var = t[1]
refcount[var] = refcount.get(var, 0) + 1
var = statement[1]
if var in refcount:
static[var] = statement[2]
- untyped.remove(var)
else:
format[var] = statement[2][1:-1]
# Reorder code tuples so internal functions are just after entry points
# Equally grotty hack to add end-of-statement delimiters.
# Put them after every newline, then remove if they're followed
# by whitespace and a continuation digit
- source = source.replace("\n", " ;\n")
+ source = source.replace("\n", " eos\n")
source = re.sub(';\n(\s*)[0-9]+', '\n\\1', source)
- # OK, now for a real kluge. Transform some adjacent groups of
- # keywords into agglomerates. This lets us get away with stuff
- # like SET LIST TO LIST that the parser would otherwise choke
- # on, because the keywords have been lifted out of where they
- # can collide with identifier space.
+ # Transform some adjacent groups of keywords into
+ # agglomerates. This lets us get away with stuff like SET LIST
+ # TO LIST that the parser would otherwise choke on, because
+ # the keywords have been lifted out of where they can collide
+ # with identifier space.
source = source.replace("SET LIST TO", "SET_LIST_TO")
source = source.replace("PRINT FORMAT", "PRINT_FORMAT")
source = source.replace("READ FORMAT", "READ_FORMAT")
+ # SLR parsing seems to be a tad too weak for the full grammar of MAD.
+ # It hits lookahead problens in parsing array references and array
+ # ranges -- doesn't know that it should look ahead far enough to
+ # see the leading paren on the subscript. So, decorate array
+ # references in a way that will make them syntactucally obvious.
+ source = re.sub(r"[A-Z][A-Z0-9]*\s*\(", r"%\g<0>", source)
# Lex or parse
if lexercise:
lex.input(source)