ccdeb16b6eea2d7d2e259ee00ef58b684e8f82fa
[platform/framework/web/lwnode.git] /
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