From 69f737764f238fc9f074b1c22b2d61a35a4dcf89 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 13 Feb 2004 08:56:59 +0000 Subject: [PATCH] This version parses wordlist correctly. --- mad | 76 ++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/mad b/mad index bd201ba..bd314e3 100755 --- a/mad +++ b/mad @@ -10,8 +10,8 @@ reserved = ('BOOLEAN', 'CONDITIONAL', 'CONTINUE', 'DATA', 'DIMENSION', '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', @@ -24,7 +24,7 @@ logops = ('NOT', 'OR', 'AND', 'THEN', 'EQV', # Tokens -t_SEMI = r';' +t_EOS = r'eos' t_COMMA = r'\,' t_ABS = r'\.ABS\.' t_POW = r'\.P\.' @@ -53,6 +53,11 @@ reserved_map = { } 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') @@ -134,11 +139,11 @@ def p_program(t): | 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 @@ -164,10 +169,10 @@ def p_statement_dimension(t): '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 @@ -248,25 +253,22 @@ def p_pause(t): ## 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) @@ -281,18 +283,19 @@ def p_remark2(t): ## 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): @@ -418,7 +421,7 @@ def p_expression_float(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 @@ -518,7 +521,6 @@ def analyze(): 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 @@ -970,16 +972,22 @@ if __name__ == '__main__': # 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) -- 2.43.0