# An implementation of Dartmouth BASIC (1964)
#
from ply import *
import basiclex
tokens = basiclex.tokens
precedence = (
('left' , 'PLUS' , 'MINUS' ),
('left' , 'TIMES' , 'DIVIDE' ),
('left' , 'POWER' ),
('right' , 'UMINUS' )
)
# A BASIC program is a series of statements. We represent the program as a
# dictionary of tuples indexed by line number.
def p_program(p):
'' 'program : program statement
| statement'' '
if len(p) == 2 and p[1 ]:
p[0 ] = {}
line, stat = p[1 ]
p[0 ][line] = stat
elif len(p) == 3 :
p[0 ] = p[1 ]
if not p[0 ]:
p[0 ] = {}
if p[2 ]:
line, stat = p[2 ]
p[0 ][line] = stat
# This catch-all rule is used for any catastrophic errors. In this case,
# we simply return nothing
def p_program_error(p):
'' 'program : error' ''
p[0 ] = None
p.parser.error = 1
# Format of all BASIC statements.
def p_statement(p):
'' 'statement : INTEGER command NEWLINE' ''
if isinstance(p[2 ], str):
print("%s %s %s" % (p[2 ], "AT LINE" , p[1 ]))
p[0 ] = None
p.parser.error = 1
else :
lineno = int(p[1 ])
p[0 ] = (lineno, p[2 ])
# Interactive statements.
def p_statement_interactive(p):
'' 'statement : RUN NEWLINE
| LIST NEWLINE
| NEW NEWLINE'' '
p[0 ] = (0 , (p[1 ], 0 ))
# Blank line number
def p_statement_blank(p):
'' 'statement : INTEGER NEWLINE' ''
p[0 ] = (0 , ('BLANK' , int(p[1 ])))
# Error handling for malformed statements
def p_statement_bad(p):
'' 'statement : INTEGER error NEWLINE' ''
print("MALFORMED STATEMENT AT LINE %s" % p[1 ])
p[0 ] = None
p.parser.error = 1
# Blank line
def p_statement_newline(p):
'' 'statement : NEWLINE' ''
p[0 ] = None
# LET statement
def p_command_let(p):
'' 'command : LET variable EQUALS expr' ''
p[0 ] = ('LET' , p[2 ], p[4 ])
def p_command_let_bad(p):
'' 'command : LET variable EQUALS error' ''
p[0 ] = "BAD EXPRESSION IN LET"
# READ statement
def p_command_read(p):
'' 'command : READ varlist' ''
p[0 ] = ('READ' , p[2 ])
def p_command_read_bad(p):
'' 'command : READ error' ''
p[0 ] = "MALFORMED VARIABLE LIST IN READ"
# DATA statement
def p_command_data(p):
'' 'command : DATA numlist' ''
p[0 ] = ('DATA' , p[2 ])
def p_command_data_bad(p):
'' 'command : DATA error' ''
p[0 ] = "MALFORMED NUMBER LIST IN DATA"
# PRINT statement
def p_command_print(p):
'' 'command : PRINT plist optend' ''
p[0 ] = ('PRINT' , p[2 ], p[3 ])
def p_command_print_bad(p):
'' 'command : PRINT error' ''
p[0 ] = "MALFORMED PRINT STATEMENT"
# Optional ending on PRINT. Either a comma (,) or semicolon (;)
def p_optend(p):
'' 'optend : COMMA
| SEMI
|'' '
if len(p) == 2 :
p[0 ] = p[1 ]
else :
p[0 ] = None
# PRINT statement with no arguments
def p_command_print_empty(p):
'' 'command : PRINT' ''
p[0 ] = ('PRINT' , [], None )
# GOTO statement
def p_command_goto(p):
'' 'command : GOTO INTEGER' ''
p[0 ] = ('GOTO' , int(p[2 ]))
def p_command_goto_bad(p):
'' 'command : GOTO error' ''
p[0 ] = "INVALID LINE NUMBER IN GOTO"
# IF-THEN statement
def p_command_if(p):
'' 'command : IF relexpr THEN INTEGER' ''
p[0 ] = ('IF' , p[2 ], int(p[4 ]))
def p_command_if_bad(p):
'' 'command : IF error THEN INTEGER' ''
p[0 ] = "BAD RELATIONAL EXPRESSION"
def p_command_if_bad2(p):
'' 'command : IF relexpr THEN error' ''
p[0 ] = "INVALID LINE NUMBER IN THEN"
# FOR statement
def p_command_for(p):
'' 'command : FOR ID EQUALS expr TO expr optstep' ''
p[0 ] = ('FOR' , p[2 ], p[4 ], p[6 ], p[7 ])
def p_command_for_bad_initial(p):
'' 'command : FOR ID EQUALS error TO expr optstep' ''
p[0 ] = "BAD INITIAL VALUE IN FOR STATEMENT"
def p_command_for_bad_final(p):
'' 'command : FOR ID EQUALS expr TO error optstep' ''
p[0 ] = "BAD FINAL VALUE IN FOR STATEMENT"
def p_command_for_bad_step(p):
'' 'command : FOR ID EQUALS expr TO expr STEP error' ''
p[0 ] = "MALFORMED STEP IN FOR STATEMENT"
# Optional STEP qualifier on FOR statement
def p_optstep(p):
'' 'optstep : STEP expr
| empty'' '
if len(p) == 3 :
p[0 ] = p[2 ]
else :
p[0 ] = None
# NEXT statement
def p_command_next(p):
'' 'command : NEXT ID' ''
p[0 ] = ('NEXT' , p[2 ])
def p_command_next_bad(p):
'' 'command : NEXT error' ''
p[0 ] = "MALFORMED NEXT"
# END statement
def p_command_end(p):
'' 'command : END' ''
p[0 ] = ('END' ,)
# REM statement
def p_command_rem(p):
'' 'command : REM' ''
p[0 ] = ('REM' , p[1 ])
# STOP statement
def p_command_stop(p):
'' 'command : STOP' ''
p[0 ] = ('STOP' ,)
# DEF statement
def p_command_def(p):
'' 'command : DEF ID LPAREN ID RPAREN EQUALS expr' ''
p[0 ] = ('FUNC' , p[2 ], p[4 ], p[7 ])
def p_command_def_bad_rhs(p):
'' 'command : DEF ID LPAREN ID RPAREN EQUALS error' ''
p[0 ] = "BAD EXPRESSION IN DEF STATEMENT"
def p_command_def_bad_arg(p):
'' 'command : DEF ID LPAREN error RPAREN EQUALS expr' ''
p[0 ] = "BAD ARGUMENT IN DEF STATEMENT"
# GOSUB statement
def p_command_gosub(p):
'' 'command : GOSUB INTEGER' ''
p[0 ] = ('GOSUB' , int(p[2 ]))
def p_command_gosub_bad(p):
'' 'command : GOSUB error' ''
p[0 ] = "INVALID LINE NUMBER IN GOSUB"
# RETURN statement
def p_command_return(p):
'' 'command : RETURN' ''
p[0 ] = ('RETURN' ,)
# DIM statement
def p_command_dim(p):
'' 'command : DIM dimlist' ''
p[0 ] = ('DIM' , p[2 ])
def p_command_dim_bad(p):
'' 'command : DIM error' ''
p[0 ] = "MALFORMED VARIABLE LIST IN DIM"
# List of variables supplied to DIM statement
def p_dimlist(p):
'' 'dimlist : dimlist COMMA dimitem
| dimitem'' '
if len(p) == 4 :
p[0 ] = p[1 ]
p[0 ].append(p[3 ])
else :
p[0 ] = [p[1 ]]
# DIM items
def p_dimitem_single(p):
'' 'dimitem : ID LPAREN INTEGER RPAREN' ''
p[0 ] = (p[1 ], eval(p[3 ]), 0 )
def p_dimitem_double(p):
'' 'dimitem : ID LPAREN INTEGER COMMA INTEGER RPAREN' ''
p[0 ] = (p[1 ], eval(p[3 ]), eval(p[5 ]))
# Arithmetic expressions
def p_expr_binary(p):
'' 'expr : expr PLUS expr
| expr MINUS expr
| expr TIMES expr
| expr DIVIDE expr
| expr POWER expr'' '
p[0 ] = ('BINOP' , p[2 ], p[1 ], p[3 ])
def p_expr_number(p):
'' 'expr : INTEGER
| FLOAT'' '
p[0 ] = ('NUM' , eval(p[1 ]))
def p_expr_variable(p):
'' 'expr : variable' ''
p[0 ] = ('VAR' , p[1 ])
def p_expr_group(p):
'' 'expr : LPAREN expr RPAREN' ''
p[0 ] = ('GROUP' , p[2 ])
def p_expr_unary(p):
'' 'expr : MINUS expr %prec UMINUS' ''
p[0 ] = ('UNARY' , '-' , p[2 ])
# Relational expressions
def p_relexpr(p):
'' 'relexpr : expr LT expr
| expr LE expr
| expr GT expr
| expr GE expr
| expr EQUALS expr
| expr NE expr'' '
p[0 ] = ('RELOP' , p[2 ], p[1 ], p[3 ])
# Variables
def p_variable(p):
'' 'variable : ID
| ID LPAREN expr RPAREN
| ID LPAREN expr COMMA expr RPAREN'' '
if len(p) == 2 :
p[0 ] = (p[1 ], None , None )
elif len(p) == 5 :
p[0 ] = (p[1 ], p[3 ], None )
else :
p[0 ] = (p[1 ], p[3 ], p[5 ])
# Builds a list of variable targets as a Python list
def p_varlist(p):
'' 'varlist : varlist COMMA variable
| variable'' '
if len(p) > 2 :
p[0 ] = p[1 ]
p[0 ].append(p[3 ])
else :
p[0 ] = [p[1 ]]
# Builds a list of numbers as a Python list
def p_numlist(p):
'' 'numlist : numlist COMMA number
| number'' '
if len(p) > 2 :
p[0 ] = p[1 ]
p[0 ].append(p[3 ])
else :
p[0 ] = [p[1 ]]
# A number. May be an integer or a float
def p_number(p):
'' 'number : INTEGER
| FLOAT'' '
p[0 ] = eval(p[1 ])
# A signed number.
def p_number_signed(p):
'' 'number : MINUS INTEGER
| MINUS FLOAT'' '
p[0 ] = eval("-" + p[2 ])
# List of targets for a print statement
# Returns a list of tuples (label,expr)
def p_plist(p):
'' 'plist : plist COMMA pitem
| pitem'' '
if len(p) > 3 :
p[0 ] = p[1 ]
p[0 ].append(p[3 ])
else :
p[0 ] = [p[1 ]]
def p_item_string(p):
'' 'pitem : STRING' ''
p[0 ] = (p[1 ][1 :-1 ], None )
def p_item_string_expr(p):
'' 'pitem : STRING expr' ''
p[0 ] = (p[1 ][1 :-1 ], p[2 ])
def p_item_expr(p):
'' 'pitem : expr' ''
p[0 ] = ("" , p[1 ])
# Empty
def p_empty(p):
'' 'empty : ' ''
# Catastrophic error handler
def p_error(p):
if not p:
print("SYNTAX ERROR AT EOF" )
bparser = yacc.yacc()
def parse(data, debug=0 ):
bparser.error = 0
p = bparser.parse(data, debug=debug)
if bparser.error:
return None
return p
Messung V0.5 in Prozent C=84 H=99 G=91
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-06-06)
¤
*© Formatika GbR, Deutschland