cscg24-lolpython

CSCG 2024 Challenge 'Can I Haz Lolpython?'
git clone https://git.sinitax.com/sinitax/cscg24-lolpython
Log | Files | Refs | sfeed.txt

basparse.py (8857B)


      1# An implementation of Dartmouth BASIC (1964)
      2#
      3
      4from ply import *
      5import basiclex
      6
      7tokens = basiclex.tokens
      8
      9precedence = (
     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
     19def 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
     37def p_program_error(p):
     38    '''program : error'''
     39    p[0] = None
     40    p.parser.error = 1
     41
     42#### Format of all BASIC statements. 
     43
     44def p_statement(p):
     45    '''statement : INTEGER command NEWLINE'''
     46    if isinstance(p[2],str):
     47        print 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
     56def 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
     63def p_statement_blank(p):
     64    '''statement : INTEGER NEWLINE'''
     65    p[0] = (0,('BLANK',int(p[1])))
     66
     67#### Error handling for malformed statements
     68
     69def p_statement_bad(p):
     70    '''statement : INTEGER error NEWLINE'''
     71    print "MALFORMED STATEMENT AT LINE", p[1]
     72    p[0] = None
     73    p.parser.error = 1
     74
     75#### Blank line
     76
     77def p_statement_newline(p):
     78    '''statement : NEWLINE'''
     79    p[0] = None
     80
     81#### LET statement
     82
     83def p_command_let(p):
     84    '''command : LET variable EQUALS expr'''
     85    p[0] = ('LET',p[2],p[4])
     86
     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):
    407    bparser.error = 0
    408    p = bparser.parse(data)
    409    if bparser.error: return None
    410    return p
    411
    412
    413
    414
    415       
    416   
    417  
    418            
    419
    420
    421
    422
    423
    424