Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/third_party/ply/example/BASIC/basparse.py
7087 views
1
# An implementation of Dartmouth BASIC (1964)
2
#
3
4
from ply import *
5
import basiclex
6
7
tokens = basiclex.tokens
8
9
precedence = (
10
('left', 'PLUS','MINUS'),
11
('left', 'TIMES','DIVIDE'),
12
('left', 'POWER'),
13
('right','UMINUS')
14
)
15
16
#### A BASIC program is a series of statements. We represent the program as a
17
#### dictionary of tuples indexed by line number.
18
19
def p_program(p):
20
'''program : program statement
21
| statement'''
22
23
if len(p) == 2 and p[1]:
24
p[0] = { }
25
line,stat = p[1]
26
p[0][line] = stat
27
elif len(p) ==3:
28
p[0] = p[1]
29
if not p[0]: p[0] = { }
30
if p[2]:
31
line,stat = p[2]
32
p[0][line] = stat
33
34
#### This catch-all rule is used for any catastrophic errors. In this case,
35
#### we simply return nothing
36
37
def p_program_error(p):
38
'''program : error'''
39
p[0] = None
40
p.parser.error = 1
41
42
#### Format of all BASIC statements.
43
44
def p_statement(p):
45
'''statement : INTEGER command NEWLINE'''
46
if isinstance(p[2],str):
47
print("%s %s %s" % (p[2],"AT LINE", p[1]))
48
p[0] = None
49
p.parser.error = 1
50
else:
51
lineno = int(p[1])
52
p[0] = (lineno,p[2])
53
54
#### Interactive statements.
55
56
def p_statement_interactive(p):
57
'''statement : RUN NEWLINE
58
| LIST NEWLINE
59
| NEW NEWLINE'''
60
p[0] = (0, (p[1],0))
61
62
#### Blank line number
63
def p_statement_blank(p):
64
'''statement : INTEGER NEWLINE'''
65
p[0] = (0,('BLANK',int(p[1])))
66
67
#### Error handling for malformed statements
68
69
def p_statement_bad(p):
70
'''statement : INTEGER error NEWLINE'''
71
print("MALFORMED STATEMENT AT LINE %s" % p[1])
72
p[0] = None
73
p.parser.error = 1
74
75
#### Blank line
76
77
def p_statement_newline(p):
78
'''statement : NEWLINE'''
79
p[0] = None
80
81
#### LET statement
82
83
def p_command_let(p):
84
'''command : LET variable EQUALS expr'''
85
p[0] = ('LET',p[2],p[4])
86
87
def p_command_let_bad(p):
88
'''command : LET variable EQUALS error'''
89
p[0] = "BAD EXPRESSION IN LET"
90
91
#### READ statement
92
93
def p_command_read(p):
94
'''command : READ varlist'''
95
p[0] = ('READ',p[2])
96
97
def p_command_read_bad(p):
98
'''command : READ error'''
99
p[0] = "MALFORMED VARIABLE LIST IN READ"
100
101
#### DATA statement
102
103
def p_command_data(p):
104
'''command : DATA numlist'''
105
p[0] = ('DATA',p[2])
106
107
def p_command_data_bad(p):
108
'''command : DATA error'''
109
p[0] = "MALFORMED NUMBER LIST IN DATA"
110
111
#### PRINT statement
112
113
def p_command_print(p):
114
'''command : PRINT plist optend'''
115
p[0] = ('PRINT',p[2],p[3])
116
117
def 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
123
def 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
134
def p_command_print_empty(p):
135
'''command : PRINT'''
136
p[0] = ('PRINT',[],None)
137
138
#### GOTO statement
139
140
def p_command_goto(p):
141
'''command : GOTO INTEGER'''
142
p[0] = ('GOTO',int(p[2]))
143
144
def p_command_goto_bad(p):
145
'''command : GOTO error'''
146
p[0] = "INVALID LINE NUMBER IN GOTO"
147
148
#### IF-THEN statement
149
150
def p_command_if(p):
151
'''command : IF relexpr THEN INTEGER'''
152
p[0] = ('IF',p[2],int(p[4]))
153
154
def p_command_if_bad(p):
155
'''command : IF error THEN INTEGER'''
156
p[0] = "BAD RELATIONAL EXPRESSION"
157
158
def p_command_if_bad2(p):
159
'''command : IF relexpr THEN error'''
160
p[0] = "INVALID LINE NUMBER IN THEN"
161
162
#### FOR statement
163
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])
167
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"
171
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"
175
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"
179
180
#### Optional STEP qualifier on FOR statement
181
182
def 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
192
def p_command_next(p):
193
'''command : NEXT ID'''
194
195
p[0] = ('NEXT',p[2])
196
197
def p_command_next_bad(p):
198
'''command : NEXT error'''
199
p[0] = "MALFORMED NEXT"
200
201
#### END statement
202
203
def p_command_end(p):
204
'''command : END'''
205
p[0] = ('END',)
206
207
#### REM statement
208
209
def p_command_rem(p):
210
'''command : REM'''
211
p[0] = ('REM',p[1])
212
213
#### STOP statement
214
215
def p_command_stop(p):
216
'''command : STOP'''
217
p[0] = ('STOP',)
218
219
#### DEF statement
220
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])
224
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"
228
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"
232
233
#### GOSUB statement
234
235
def p_command_gosub(p):
236
'''command : GOSUB INTEGER'''
237
p[0] = ('GOSUB',int(p[2]))
238
239
def p_command_gosub_bad(p):
240
'''command : GOSUB error'''
241
p[0] = "INVALID LINE NUMBER IN GOSUB"
242
243
#### RETURN statement
244
245
def p_command_return(p):
246
'''command : RETURN'''
247
p[0] = ('RETURN',)
248
249
#### DIM statement
250
251
def p_command_dim(p):
252
'''command : DIM dimlist'''
253
p[0] = ('DIM',p[2])
254
255
def 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
261
def 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
272
def p_dimitem_single(p):
273
'''dimitem : ID LPAREN INTEGER RPAREN'''
274
p[0] = (p[1],eval(p[3]),0)
275
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]))
279
280
#### Arithmetic expressions
281
282
def 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
291
def p_expr_number(p):
292
'''expr : INTEGER
293
| FLOAT'''
294
p[0] = ('NUM',eval(p[1]))
295
296
def p_expr_variable(p):
297
'''expr : variable'''
298
p[0] = ('VAR',p[1])
299
300
def p_expr_group(p):
301
'''expr : LPAREN expr RPAREN'''
302
p[0] = ('GROUP',p[2])
303
304
def p_expr_unary(p):
305
'''expr : MINUS expr %prec UMINUS'''
306
p[0] = ('UNARY','-',p[2])
307
308
#### Relational expressions
309
310
def 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
321
def 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
334
def 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
346
def 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
358
def p_number(p):
359
'''number : INTEGER
360
| FLOAT'''
361
p[0] = eval(p[1])
362
363
#### A signed number.
364
365
def 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
373
def 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
382
def p_item_string(p):
383
'''pitem : STRING'''
384
p[0] = (p[1][1:-1],None)
385
386
def p_item_string_expr(p):
387
'''pitem : STRING expr'''
388
p[0] = (p[1][1:-1],p[2])
389
390
def p_item_expr(p):
391
'''pitem : expr'''
392
p[0] = ("",p[1])
393
394
#### Empty
395
396
def p_empty(p):
397
'''empty : '''
398
399
#### Catastrophic error handler
400
def p_error(p):
401
if not p:
402
print("SYNTAX ERROR AT EOF")
403
404
bparser = yacc.yacc()
405
406
def 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
426