basparse.py revision 6498
112243Sgabeblack@google.com# An implementation of Dartmouth BASIC (1964)
212243Sgabeblack@google.com#
312243Sgabeblack@google.com
412243Sgabeblack@google.comfrom ply import *
512243Sgabeblack@google.comimport basiclex
612243Sgabeblack@google.com
712243Sgabeblack@google.comtokens = basiclex.tokens
812243Sgabeblack@google.com
912243Sgabeblack@google.comprecedence = (
1012243Sgabeblack@google.com               ('left', 'PLUS','MINUS'),
1112243Sgabeblack@google.com               ('left', 'TIMES','DIVIDE'),
1212243Sgabeblack@google.com               ('left', 'POWER'),
1312243Sgabeblack@google.com               ('right','UMINUS')
1412243Sgabeblack@google.com)
1512243Sgabeblack@google.com
1612243Sgabeblack@google.com#### A BASIC program is a series of statements.  We represent the program as a
1712243Sgabeblack@google.com#### dictionary of tuples indexed by line number.
1812243Sgabeblack@google.com
1912243Sgabeblack@google.comdef p_program(p):
2012243Sgabeblack@google.com    '''program : program statement
2112243Sgabeblack@google.com               | statement'''
2212243Sgabeblack@google.com
2312243Sgabeblack@google.com    if len(p) == 2 and p[1]:
2412243Sgabeblack@google.com       p[0] = { }
2512243Sgabeblack@google.com       line,stat = p[1]
2612243Sgabeblack@google.com       p[0][line] = stat
2712243Sgabeblack@google.com    elif len(p) ==3:
2812243Sgabeblack@google.com       p[0] = p[1]
2912243Sgabeblack@google.com       if not p[0]: p[0] = { }
3012243Sgabeblack@google.com       if p[2]:
3112243Sgabeblack@google.com           line,stat = p[2]
3212243Sgabeblack@google.com           p[0][line] = stat
3312243Sgabeblack@google.com
3412243Sgabeblack@google.com#### This catch-all rule is used for any catastrophic errors.  In this case,
3512243Sgabeblack@google.com#### we simply return nothing
3612243Sgabeblack@google.com
3712243Sgabeblack@google.comdef p_program_error(p):
3812243Sgabeblack@google.com    '''program : error'''
3912243Sgabeblack@google.com    p[0] = None
4012243Sgabeblack@google.com    p.parser.error = 1
4112243Sgabeblack@google.com
4212243Sgabeblack@google.com#### Format of all BASIC statements.
4312243Sgabeblack@google.com
4412243Sgabeblack@google.comdef p_statement(p):
4512243Sgabeblack@google.com    '''statement : INTEGER command NEWLINE'''
4612243Sgabeblack@google.com    if isinstance(p[2],str):
4712243Sgabeblack@google.com        print("%s %s %s" % (p[2],"AT LINE", p[1]))
4812243Sgabeblack@google.com        p[0] = None
4912243Sgabeblack@google.com        p.parser.error = 1
5012243Sgabeblack@google.com    else:
5112243Sgabeblack@google.com        lineno = int(p[1])
5212243Sgabeblack@google.com        p[0] = (lineno,p[2])
5312243Sgabeblack@google.com
5412243Sgabeblack@google.com#### Interactive statements.
5512243Sgabeblack@google.com
5612243Sgabeblack@google.comdef p_statement_interactive(p):
5712243Sgabeblack@google.com    '''statement : RUN NEWLINE
5812243Sgabeblack@google.com                 | LIST NEWLINE
5912243Sgabeblack@google.com                 | NEW NEWLINE'''
6012243Sgabeblack@google.com    p[0] = (0, (p[1],0))
6112243Sgabeblack@google.com
6212243Sgabeblack@google.com#### Blank line number
6312243Sgabeblack@google.comdef p_statement_blank(p):
6412243Sgabeblack@google.com    '''statement : INTEGER NEWLINE'''
6512243Sgabeblack@google.com    p[0] = (0,('BLANK',int(p[1])))
6612243Sgabeblack@google.com
6712243Sgabeblack@google.com#### Error handling for malformed statements
6812243Sgabeblack@google.com
6912243Sgabeblack@google.comdef p_statement_bad(p):
7012243Sgabeblack@google.com    '''statement : INTEGER error NEWLINE'''
7112243Sgabeblack@google.com    print("MALFORMED STATEMENT AT LINE %s" % p[1])
7212243Sgabeblack@google.com    p[0] = None
7312243Sgabeblack@google.com    p.parser.error = 1
7412243Sgabeblack@google.com
7512244Sgabeblack@google.com#### Blank line
7612244Sgabeblack@google.com
7712243Sgabeblack@google.comdef p_statement_newline(p):
7812243Sgabeblack@google.com    '''statement : NEWLINE'''
7912243Sgabeblack@google.com    p[0] = None
8012243Sgabeblack@google.com
8112243Sgabeblack@google.com#### LET statement
8212243Sgabeblack@google.com
8312243Sgabeblack@google.comdef p_command_let(p):
8412243Sgabeblack@google.com    '''command : LET variable EQUALS expr'''
8512243Sgabeblack@google.com    p[0] = ('LET',p[2],p[4])
8612243Sgabeblack@google.com
87def p_command_let_bad(p):
88    '''command : LET variable EQUALS error'''
89    p[0] = "BAD EXPRESSION IN LET"
90
91#### READ statement
92
93def p_command_read(p):
94    '''command : READ varlist'''
95    p[0] = ('READ',p[2])
96
97def p_command_read_bad(p):
98    '''command : READ error'''
99    p[0] = "MALFORMED VARIABLE LIST IN READ"
100
101#### DATA statement
102
103def p_command_data(p):
104    '''command : DATA numlist'''
105    p[0] = ('DATA',p[2])
106
107def p_command_data_bad(p):
108    '''command : DATA error'''
109    p[0] = "MALFORMED NUMBER LIST IN DATA"
110
111#### PRINT statement
112
113def p_command_print(p):
114    '''command : PRINT plist optend'''
115    p[0] = ('PRINT',p[2],p[3])
116
117def p_command_print_bad(p):
118    '''command : PRINT error'''
119    p[0] = "MALFORMED PRINT STATEMENT"
120
121#### Optional ending on PRINT. Either a comma (,) or semicolon (;)
122
123def p_optend(p):
124    '''optend : COMMA
125              | SEMI
126              |'''
127    if len(p)  == 2:
128         p[0] = p[1]
129    else:
130         p[0] = None
131
132#### PRINT statement with no arguments
133
134def p_command_print_empty(p):
135    '''command : PRINT'''
136    p[0] = ('PRINT',[],None)
137
138#### GOTO statement
139
140def p_command_goto(p):
141    '''command : GOTO INTEGER'''
142    p[0] = ('GOTO',int(p[2]))
143
144def p_command_goto_bad(p):
145    '''command : GOTO error'''
146    p[0] = "INVALID LINE NUMBER IN GOTO"
147
148#### IF-THEN statement
149
150def p_command_if(p):
151    '''command : IF relexpr THEN INTEGER'''
152    p[0] = ('IF',p[2],int(p[4]))
153
154def p_command_if_bad(p):
155    '''command : IF error THEN INTEGER'''
156    p[0] = "BAD RELATIONAL EXPRESSION"
157
158def p_command_if_bad2(p):
159    '''command : IF relexpr THEN error'''
160    p[0] = "INVALID LINE NUMBER IN THEN"
161
162#### FOR statement
163
164def 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])
167
168def p_command_for_bad_initial(p):
169    '''command : FOR ID EQUALS error TO expr optstep'''
170    p[0] = "BAD INITIAL VALUE IN FOR STATEMENT"
171
172def p_command_for_bad_final(p):
173    '''command : FOR ID EQUALS expr TO error optstep'''
174    p[0] = "BAD FINAL VALUE IN FOR STATEMENT"
175
176def p_command_for_bad_step(p):
177    '''command : FOR ID EQUALS expr TO expr STEP error'''
178    p[0] = "MALFORMED STEP IN FOR STATEMENT"
179
180#### Optional STEP qualifier on FOR statement
181
182def p_optstep(p):
183    '''optstep : STEP expr
184               | empty'''
185    if len(p) == 3:
186       p[0] = p[2]
187    else:
188       p[0] = None
189
190#### NEXT statement
191
192def p_command_next(p):
193    '''command : NEXT ID'''
194
195    p[0] = ('NEXT',p[2])
196
197def p_command_next_bad(p):
198    '''command : NEXT error'''
199    p[0] = "MALFORMED NEXT"
200
201#### END statement
202
203def p_command_end(p):
204    '''command : END'''
205    p[0] = ('END',)
206
207#### REM statement
208
209def p_command_rem(p):
210    '''command : REM'''
211    p[0] = ('REM',p[1])
212
213#### STOP statement
214
215def p_command_stop(p):
216    '''command : STOP'''
217    p[0] = ('STOP',)
218
219#### DEF statement
220
221def p_command_def(p):
222    '''command : DEF ID LPAREN ID RPAREN EQUALS expr'''
223    p[0] = ('FUNC',p[2],p[4],p[7])
224
225def p_command_def_bad_rhs(p):
226    '''command : DEF ID LPAREN ID RPAREN EQUALS error'''
227    p[0] = "BAD EXPRESSION IN DEF STATEMENT"
228
229def p_command_def_bad_arg(p):
230    '''command : DEF ID LPAREN error RPAREN EQUALS expr'''
231    p[0] = "BAD ARGUMENT IN DEF STATEMENT"
232
233#### GOSUB statement
234
235def p_command_gosub(p):
236    '''command : GOSUB INTEGER'''
237    p[0] = ('GOSUB',int(p[2]))
238
239def p_command_gosub_bad(p):
240    '''command : GOSUB error'''
241    p[0] = "INVALID LINE NUMBER IN GOSUB"
242
243#### RETURN statement
244
245def p_command_return(p):
246    '''command : RETURN'''
247    p[0] = ('RETURN',)
248
249#### DIM statement
250
251def p_command_dim(p):
252    '''command : DIM dimlist'''
253    p[0] = ('DIM',p[2])
254
255def p_command_dim_bad(p):
256    '''command : DIM error'''
257    p[0] = "MALFORMED VARIABLE LIST IN DIM"
258
259#### List of variables supplied to DIM statement
260
261def p_dimlist(p):
262    '''dimlist : dimlist COMMA dimitem
263               | dimitem'''
264    if len(p) == 4:
265        p[0] = p[1]
266        p[0].append(p[3])
267    else:
268        p[0] = [p[1]]
269
270#### DIM items
271
272def p_dimitem_single(p):
273    '''dimitem : ID LPAREN INTEGER RPAREN'''
274    p[0] = (p[1],eval(p[3]),0)
275
276def p_dimitem_double(p):
277    '''dimitem : ID LPAREN INTEGER COMMA INTEGER RPAREN'''
278    p[0] = (p[1],eval(p[3]),eval(p[5]))
279
280#### Arithmetic expressions
281
282def p_expr_binary(p):
283    '''expr : expr PLUS expr
284            | expr MINUS expr
285            | expr TIMES expr
286            | expr DIVIDE expr
287            | expr POWER expr'''
288
289    p[0] = ('BINOP',p[2],p[1],p[3])
290
291def p_expr_number(p):
292    '''expr : INTEGER
293            | FLOAT'''
294    p[0] = ('NUM',eval(p[1]))
295
296def p_expr_variable(p):
297    '''expr : variable'''
298    p[0] = ('VAR',p[1])
299
300def p_expr_group(p):
301    '''expr : LPAREN expr RPAREN'''
302    p[0] = ('GROUP',p[2])
303
304def p_expr_unary(p):
305    '''expr : MINUS expr %prec UMINUS'''
306    p[0] = ('UNARY','-',p[2])
307
308#### Relational expressions
309
310def p_relexpr(p):
311    '''relexpr : expr LT expr
312               | expr LE expr
313               | expr GT expr
314               | expr GE expr
315               | expr EQUALS expr
316               | expr NE expr'''
317    p[0] = ('RELOP',p[2],p[1],p[3])
318
319#### Variables
320
321def p_variable(p):
322    '''variable : ID
323              | ID LPAREN expr RPAREN
324              | ID LPAREN expr COMMA expr RPAREN'''
325    if len(p) == 2:
326       p[0] = (p[1],None,None)
327    elif len(p) == 5:
328       p[0] = (p[1],p[3],None)
329    else:
330       p[0] = (p[1],p[3],p[5])
331
332#### Builds a list of variable targets as a Python list
333
334def p_varlist(p):
335    '''varlist : varlist COMMA variable
336               | variable'''
337    if len(p) > 2:
338       p[0] = p[1]
339       p[0].append(p[3])
340    else:
341       p[0] = [p[1]]
342
343
344#### Builds a list of numbers as a Python list
345
346def p_numlist(p):
347    '''numlist : numlist COMMA number
348               | number'''
349
350    if len(p) > 2:
351       p[0] = p[1]
352       p[0].append(p[3])
353    else:
354       p[0] = [p[1]]
355
356#### A number. May be an integer or a float
357
358def p_number(p):
359    '''number  : INTEGER
360               | FLOAT'''
361    p[0] = eval(p[1])
362
363#### A signed number.
364
365def p_number_signed(p):
366    '''number  : MINUS INTEGER
367               | MINUS FLOAT'''
368    p[0] = eval("-"+p[2])
369
370#### List of targets for a print statement
371#### Returns a list of tuples (label,expr)
372
373def p_plist(p):
374    '''plist   : plist COMMA pitem
375               | pitem'''
376    if len(p) > 3:
377       p[0] = p[1]
378       p[0].append(p[3])
379    else:
380       p[0] = [p[1]]
381
382def p_item_string(p):
383    '''pitem : STRING'''
384    p[0] = (p[1][1:-1],None)
385
386def p_item_string_expr(p):
387    '''pitem : STRING expr'''
388    p[0] = (p[1][1:-1],p[2])
389
390def p_item_expr(p):
391    '''pitem : expr'''
392    p[0] = ("",p[1])
393
394#### Empty
395
396def p_empty(p):
397    '''empty : '''
398
399#### Catastrophic error handler
400def p_error(p):
401    if not p:
402        print("SYNTAX ERROR AT EOF")
403
404bparser = yacc.yacc()
405
406def parse(data,debug=0):
407    bparser.error = 0
408    p = bparser.parse(data,debug=debug)
409    if bparser.error: return None
410    return p
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425