1 # An implementation of Dartmouth BASIC (1964)
7 tokens
= basiclex
.tokens
10 ('left', 'PLUS','MINUS'),
11 ('left', 'TIMES','DIVIDE'),
16 #### A BASIC program is a series of statements. We represent the program as a
17 #### dictionary of tuples indexed by line number.
20 '''program : program statement
23 if len(p
) == 2 and p
[1]:
29 if not p
[0]: p
[0] = { }
34 #### This catch-all rule is used for any catastrophic errors. In this case,
35 #### we simply return nothing
37 def p_program_error(p
):
42 #### Format of all BASIC statements.
45 '''statement : INTEGER command NEWLINE'''
46 if isinstance(p
[2],str):
47 print p
[2],"AT LINE", p
[1]
54 #### Interactive statements.
56 def p_statement_interactive(p
):
57 '''statement : RUN NEWLINE
62 #### Blank line number
63 def p_statement_blank(p
):
64 '''statement : INTEGER NEWLINE'''
65 p
[0] = (0,('BLANK',int(p
[1])))
67 #### Error handling for malformed statements
69 def p_statement_bad(p
):
70 '''statement : INTEGER error NEWLINE'''
71 print "MALFORMED STATEMENT AT LINE", p
[1]
77 def p_statement_newline(p
):
78 '''statement : NEWLINE'''
84 '''command : LET variable EQUALS expr'''
85 p
[0] = ('LET',p
[2],p
[4])
87 def p_command_let_bad(p
):
88 '''command : LET variable EQUALS error'''
89 p
[0] = "BAD EXPRESSION IN LET"
93 def p_command_read(p
):
94 '''command : READ varlist'''
97 def p_command_read_bad(p
):
98 '''command : READ error'''
99 p
[0] = "MALFORMED VARIABLE LIST IN READ"
103 def p_command_data(p
):
104 '''command : DATA numlist'''
107 def p_command_data_bad(p
):
108 '''command : DATA error'''
109 p
[0] = "MALFORMED NUMBER LIST IN DATA"
113 def p_command_print(p
):
114 '''command : PRINT plist optend'''
115 p
[0] = ('PRINT',p
[2],p
[3])
117 def p_command_print_bad(p
):
118 '''command : PRINT error'''
119 p
[0] = "MALFORMED PRINT STATEMENT"
121 #### Optional ending on PRINT. Either a comma (,) or semicolon (;)
132 #### PRINT statement with no arguments
134 def p_command_print_empty(p
):
135 '''command : PRINT'''
136 p
[0] = ('PRINT',[],None)
140 def p_command_goto(p
):
141 '''command : GOTO INTEGER'''
142 p
[0] = ('GOTO',int(p
[2]))
144 def p_command_goto_bad(p
):
145 '''command : GOTO error'''
146 p
[0] = "INVALID LINE NUMBER IN GOTO"
148 #### IF-THEN statement
151 '''command : IF relexpr THEN INTEGER'''
152 p
[0] = ('IF',p
[2],int(p
[4]))
154 def p_command_if_bad(p
):
155 '''command : IF error THEN INTEGER'''
156 p
[0] = "BAD RELATIONAL EXPRESSION"
158 def p_command_if_bad2(p
):
159 '''command : IF relexpr THEN error'''
160 p
[0] = "INVALID LINE NUMBER IN THEN"
164 def p_command_for(p
):
165 '''command : FOR ID EQUALS expr TO expr optstep'''
166 p
[0] = ('FOR',p
[2],p
[4],p
[6],p
[7])
168 def p_command_for_bad_initial(p
):
169 '''command : FOR ID EQUALS error TO expr optstep'''
170 p
[0] = "BAD INITIAL VALUE IN FOR STATEMENT"
172 def p_command_for_bad_final(p
):
173 '''command : FOR ID EQUALS expr TO error optstep'''
174 p
[0] = "BAD FINAL VALUE IN FOR STATEMENT"
176 def p_command_for_bad_step(p
):
177 '''command : FOR ID EQUALS expr TO expr STEP error'''
178 p
[0] = "MALFORMED STEP IN FOR STATEMENT"
180 #### Optional STEP qualifier on FOR statement
183 '''optstep : STEP expr
192 def p_command_next(p
):
193 '''command : NEXT ID'''
197 def p_command_next_bad(p
):
198 '''command : NEXT error'''
199 p
[0] = "MALFORMED NEXT"
203 def p_command_end(p
):
209 def p_command_rem(p
):
215 def p_command_stop(p
):
221 def p_command_def(p
):
222 '''command : DEF ID LPAREN ID RPAREN EQUALS expr'''
223 p
[0] = ('FUNC',p
[2],p
[4],p
[7])
225 def p_command_def_bad_rhs(p
):
226 '''command : DEF ID LPAREN ID RPAREN EQUALS error'''
227 p
[0] = "BAD EXPRESSION IN DEF STATEMENT"
229 def p_command_def_bad_arg(p
):
230 '''command : DEF ID LPAREN error RPAREN EQUALS expr'''
231 p
[0] = "BAD ARGUMENT IN DEF STATEMENT"
235 def p_command_gosub(p
):
236 '''command : GOSUB INTEGER'''
237 p
[0] = ('GOSUB',int(p
[2]))
239 def p_command_gosub_bad(p
):
240 '''command : GOSUB error'''
241 p
[0] = "INVALID LINE NUMBER IN GOSUB"
243 #### RETURN statement
245 def p_command_return(p
):
246 '''command : RETURN'''
251 def p_command_dim(p
):
252 '''command : DIM dimlist'''
255 def p_command_dim_bad(p
):
256 '''command : DIM error'''
257 p
[0] = "MALFORMED VARIABLE LIST IN DIM"
259 #### List of variables supplied to DIM statement
262 '''dimlist : dimlist COMMA dimitem
272 def p_dimitem_single(p
):
273 '''dimitem : ID LPAREN INTEGER RPAREN'''
274 p
[0] = (p
[1],eval(p
[3]),0)
276 def p_dimitem_double(p
):
277 '''dimitem : ID LPAREN INTEGER COMMA INTEGER RPAREN'''
278 p
[0] = (p
[1],eval(p
[3]),eval(p
[5]))
280 #### Arithmetic expressions
282 def p_expr_binary(p
):
283 '''expr : expr PLUS expr
289 p
[0] = ('BINOP',p
[2],p
[1],p
[3])
291 def p_expr_number(p
):
294 p
[0] = ('NUM',eval(p
[1]))
296 def p_expr_variable(p
):
297 '''expr : variable'''
301 '''expr : LPAREN expr RPAREN'''
302 p
[0] = ('GROUP',p
[2])
305 '''expr : MINUS expr %prec UMINUS'''
306 p
[0] = ('UNARY','-',p
[2])
308 #### Relational expressions
311 '''relexpr : expr LT expr
317 p
[0] = ('RELOP',p
[2],p
[1],p
[3])
323 | ID LPAREN expr RPAREN
324 | ID LPAREN expr COMMA expr RPAREN'''
326 p
[0] = (p
[1],None,None)
328 p
[0] = (p
[1],p
[3],None)
330 p
[0] = (p
[1],p
[3],p
[5])
332 #### Builds a list of variable targets as a Python list
335 '''varlist : varlist COMMA variable
344 #### Builds a list of numbers as a Python list
347 '''numlist : numlist COMMA number
356 #### A number. May be an integer or a float
363 #### A signed number.
365 def p_number_signed(p
):
366 '''number : MINUS INTEGER
368 p
[0] = eval("-"+p
[2])
370 #### List of targets for a print statement
371 #### Returns a list of tuples (label,expr)
374 '''plist : plist COMMA pitem
382 def p_item_string(p
):
384 p
[0] = (p
[1][1:-1],None)
386 def p_item_string_expr(p
):
387 '''pitem : STRING expr'''
388 p
[0] = (p
[1][1:-1],p
[2])
399 #### Catastrophic error handler
402 print "SYNTAX ERROR AT EOF"
404 bparser
= yacc
.yacc()
408 p
= bparser
.parse(data
)
409 if bparser
.error
: return None