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