Skip to content

Instantly share code, notes, and snippets.

@scottleedavis
Created October 2, 2019 03:48
Show Gist options
  • Save scottleedavis/a8c849cfdc7f1e752147a7af0932b8ed to your computer and use it in GitHub Desktop.
Save scottleedavis/a8c849cfdc7f1e752147a7af0932b8ed to your computer and use it in GitHub Desktop.
an example python script that parses and runs BASIC
#from https://repl.it/@meesh/BASIC-interpreter-in-Python
program = '''
10 REM POWER TABLE
11 DATA 8, 4
15 READ N0, P0
20 PRINT "N",
25 FOR P = 2 to P0
30 PRINT "N ^" P,
35 NEXT P
40 PRINT "SUM"
45 LET S = 0
50 FOR N = 2 TO N0
55 PRINT N,
60 FOR P = 2 TO P0
65 LET S = S + N ^ P
70 PRINT N ^ P,
75 NEXT P
80 PRINT S
85 NEXT N
99 END
'''
import re
tokenize = re.compile(
r'''\d* \.? \d+ (?: E -? \d+)? | # number
SIN|COS|TAN|ATN|EXP|ABS|LOG|SQR|RND|INT|FN[A-Z]| # functions (including user-defined FNA-FNZ)
LET|READ|DATA|PRINT|GOTO|IF|FOR|NEXT|END|STOP | # keywords
DEF|GOSUB|RETURN|DIM|REM|TO|THEN|STEP | # more keywords
[A-Z]\d? | # variable names (letter optionally followed by a digit)
" .*? " | # labels (strings in double quotes)
<>|>=|<= | # multi-character relational operators
\S # any non-space single character ''',
re.VERBOSE).findall
tokens = [] # Global variable to hold a list of tokens
def tokenizer(line):
"Return a list of the tokens on this line, handling spaces properly, and upper-casing."
return tokenize(remove_spaces(line).upper())
def peek():
"Return the first token in the global `tokens`, or None if we are at the end of the line."
return (tokens[0] if tokens else None)
def pop(constraint=None):
"""Remove and return the first token in `tokens`, or return None if first token fails a constraint.
`constraint` can be None, a literal string (e.g. pop('=')), or a predicate (e.g. pop(is_varname))."""
if constraint is None or (peek() == constraint) or (callable(constraint) and constraint(peek())):
return tokens.pop(0)
def remove_spaces(line):
"Remove white space from line, except space inside double quotes."
return ''.join(tokenize(line))
def lines(text):
"A list of the non-empty lines in a text."
return [line for line in text.splitlines() if line]
def test_tokenizer():
global tokens
assert tokenizer('X-1') == ['X', '-', '1'] # Numbers don't have a leading minus sign, so this isn't ['X', '-1']
assert tokenizer('PRINT "HELLO WORLD"') == ['PRINT', '"HELLO WORLD"']
assert tokenizer('10 GOTO 99') == tokenizer('10GOTO99') == tokenizer('10 GO TO 99') == ['10', 'GOTO', '99']
assert (tokenizer('100 PRINT "HELLO WORLD", SIN(X) ^ 2') ==
['100', 'PRINT', '"HELLO WORLD"', ',', 'SIN', '(', 'X', ')', '^', '2'])
assert (tokenizer('100IFX1+123.4+E1-12.3E4 <> 1.2E-34*-12E34+1+"HI" THEN99') ==
['100', 'IF', 'X1', '+', '123.4', '+', 'E1', '-', '12.3E4', '<>',
'1.2E-34', '*', '-', '12E34', '+', '1', '+', '"HI"', 'THEN', '99'])
assert remove_spaces('10 GO TO 99') == '10GOTO99'
assert remove_spaces('100 PRINT "HELLO WORLD", SIN(X) ^ 2') == '100PRINT"HELLO WORLD",SIN(X)^2'
assert lines('one line') == ['one line']
assert lines(program) == [
'10 REM POWER TABLE',
'11 DATA 8, 4',
'15 READ N0, P0',
'20 PRINT "N",',
'25 FOR P = 2 to P0',
'30 PRINT "N ^" P,',
'35 NEXT P',
'40 PRINT "SUM"',
'45 LET S = 0',
'50 FOR N = 2 TO N0',
'55 PRINT N,',
'60 FOR P = 2 TO P0',
'65 LET S = S + N ^ P',
'70 PRINT N ^ P,',
'75 NEXT P',
'80 PRINT S',
'85 NEXT N',
'99 END']
assert [tokenizer(line) for line in lines(program)] == [
['10', 'REM', 'P', 'O', 'W', 'E', 'R', 'T', 'A', 'B', 'L', 'E'],
['11', 'DATA', '8', ',', '4'],
['15', 'READ', 'N0', ',', 'P0'],
['20', 'PRINT', '"N"', ','],
['25', 'FOR', 'P', '=', '2', 'TO', 'P0'],
['30', 'PRINT', '"N ^"', 'P', ','],
['35', 'NEXT', 'P'],
['40', 'PRINT', '"SUM"'],
['45', 'LET', 'S', '=', '0'],
['50', 'FOR', 'N', '=', '2', 'TO', 'N0'],
['55', 'PRINT', 'N', ','],
['60', 'FOR', 'P', '=', '2', 'TO', 'P0'],
['65', 'LET', 'S', '=', 'S', '+', 'N', '^', 'P'],
['70', 'PRINT', 'N', '^', 'P', ','],
['75', 'NEXT', 'P'],
['80', 'PRINT', 'S'],
['85', 'NEXT', 'N'],
['99', 'END']]
tokens = tokenizer('10 GO TO 99')
assert peek() == '10'
assert pop() == '10'
assert peek() == 'GOTO'
assert pop() == 'GOTO'
assert peek() == '99'
assert pop(str.isalpha) == None # '99' is not alphabetic
assert pop('98.6') == None # '99' is not '98.6'
assert peek() == '99'
assert pop(str.isnumeric) == '99' # '99' is numeric
assert peek() is None and not tokens
return 'ok'
def Grammar():
return {
'LET': [variable, '=', expression],
'READ': [list_of(variable)],
'DATA': [list_of(number)],
'PRINT': [labels_and_expressions],
'GOTO': [linenumber],
'IF': [expression, relational, expression, 'THEN', linenumber],
'FOR': [varname, '=', expression, 'TO', expression, step],
'NEXT': [varname],
'END': [],
'STOP': [],
'DEF': [funcname, '(', varname, ')', '=', expression],
'GOSUB': [linenumber],
'RETURN': [],
'DIM': [list_of(variable)],
'REM': [anycharacters]
}
def statement():
"Parse a BASIC statement from `tokens`."
num = linenumber()
typ = pop(is_stmt_type) or fail('unknown statement type')
args = []
for c in grammar[typ]: # For each constituent of rule, call if callable or match if literal string
if callable(c):
args.append(c())
else:
pop(c) or fail('expected ' + repr(c))
return Stmt(num, typ, args)
def linenumber(): return (int(pop()) if peek().isnumeric() else fail('missing line number'))
def number(): return (-1 if pop('-') else +1) * float(pop()) # Optional minus sign before number
def step(): return (expression() if pop('STEP') else 1) # 1 is the default step
def relational(): return pop(is_relational) or fail('expected a relational operator')
def varname(): return pop(is_varname) or fail('expected a variable name')
def funcname(): return pop(is_funcname) or fail('expected a function name')
def anycharacters(): tokens.clear() # The tokens in a REM statement don't matter, so just clear them
def is_stmt_type(x): return isinstance(x, str) and x in grammar # LET, READ, ...
def is_funcname(x): return isinstance(x, str) and len(x) == 3 and x.isalpha() # SIN, COS, FNA, FNB, ...
def is_varname(x): return isinstance(x, str) and len(x) in (1, 2) and x[0].isalpha() # A, A1, A2, B, ...
def is_label(x): return isinstance(x, str) and x.startswith('"') # "HELLO WORLD", ...
def is_relational(x): return isinstance(x, str) and x in ('<', '=', '>', '<=', '<>', '>=')
def is_number(x): return isinstance(x, str) and x and x[0] in '.0123456789' # '3', '.14', ...
def variable():
"Parse a possibly subscripted variable e.g. 'X3' or 'A(I)' or 'M(2*I, 3)'."
V = varname()
if pop('('):
indexes = list_of(expression)()
pop(')') or fail('expected ")" to close subscript')
return Subscript(V, indexes) # E.g. 'A(I)' => Subscript('A', ['I'])
else:
return V # E.g. 'X3'
class list_of:
"list_of(category) is a callable that parses a comma-separated list of <category>"
def __init__(self, category): self.category = category
def __call__(self):
result = ([self.category()] if tokens else [])
while pop(','):
result.append(self.category())
return result
def parse(program): return sorted(map(parse_line, lines(program)))
def parse_line(line):
"Return a Stmt(linenumber, statement_type, arguments)."
global tokens
tokens = tokenizer(line)
try:
stmt = statement()
if tokens: fail('extra tokens at end of line')
return stmt
except SyntaxError as err:
print("Error in line '{}' at '{}': {}".format(line, ' '.join(tokens), err))
return Stmt(0, 'REM', []) # Have to return something: a dummy statement
def fail(message): raise SyntaxError(message)
from collections import namedtuple, defaultdict, deque
Stmt = namedtuple('Stmt', 'num, typ, args') # Statement: '20 GOTO 99' => Stmt(20, 'GOTO', 99)
Subscript = namedtuple('Subscript', 'var, indexes') # Subscripted reference: 'A(I)' => Subscript('A', ['I'])
Funcall = namedtuple('Funcall', 'f, x') # Function call: 'SQR(X)' => Funcall('SQR', 'X')
Opcall = namedtuple('Opcall', 'x, op, y') # Infix operation: 'X + 1' => Opcall('X', '+', 1)
ForState = namedtuple('ForState', 'continu, end, step') # Data used to control a FOR loop variable
class Function(namedtuple('_', 'parm, body')):
"User-defined callable function; 'DEF FNC(X) = X ^ 3' => functions['FNC'] = Function('X', Opcall('X', '^', 3))"
def __call__(self, value):
variables[self.parm] = value # Global assignment to the parameter
return evalu(self.body)
def labels_and_expressions():
"Parse a sequence of label / comma / semicolon / expression (for PRINT statement)."
result = []
while tokens:
item = pop(is_label) or pop(',') or pop(';') or expression()
result.append(item)
return result
def expression(prec=1):
"Parse an expression: a primary and any [op expression]* pairs with precedence(op) >= prec."
exp = primary() # 'A' => 'A'
while precedence(peek()) >= prec:
op = pop()
rhs = expression(precedence(op) + associativity(op))
exp = Opcall(exp, op, rhs) # 'A + B' => Opcall('A', '+', 'B')
return exp
def primary():
"Parse a primary expression (no infix op except maybe within parens)."
if is_number(peek()): # '1.23' => 1.23
return number()
elif is_varname(peek()): # X or A(I) or M(I+1, J)
return variable()
elif is_funcname(peek()): # SIN(X) => Funcall('SIN', 'X')
return Funcall(pop(), primary())
elif pop('-'): # '-X' => Funcall('NEG', 'X')
return Funcall('NEG', primary())
elif pop('('): # '(X + 1)' => Opcall('X', '+', 1)
exp = expression()
pop(')') or fail('expected ")" to end expression')
return exp
else:
return fail('unknown expression')
def precedence(op): return (3 if op == '^' else 2 if op in ('*', '/') else 1 if op in ('+', '-') else 0)
def associativity(op): return (0 if op == '^' else 1)
def test_parse(text, result, category=expression):
"Test that text can be parsed as a category to yield the semantic result, with no tokens left over."
global tokens
tokens = tokenizer(text)
return category() == result and not tokens
def test_parser():
assert is_funcname('SIN') and is_funcname('FNZ') # Function names are three letters
assert not is_funcname('X') and not is_funcname('')
assert is_varname('X') and is_varname('A2') # Variables names are one letter and an optional digit
assert not is_varname('FNZ') and not is_varname('A10') and not is_varname('')
assert is_relational('>') and is_relational('>=') and not is_relational('+')
assert test_parse('A + B * X + C', Opcall(Opcall('A', '+', Opcall('B', '*', 'X')), '+', 'C'))
assert test_parse('A + B + X + C', Opcall(Opcall(Opcall('A', '+', 'B'), '+', 'X'), '+', 'C'))
assert test_parse('SIN(X)^2', Opcall(Funcall('SIN', 'X'), '^', 2))
assert test_parse('10 ^ 2 ^ 3', Opcall(10, '^', Opcall(2, '^', 3))) # right associative
assert test_parse('10 - 2 - 3', Opcall(Opcall(10, '-', 2), '-', 3)) # left associative
assert test_parse('A(I)+M(I, J)', Opcall(Subscript(var='A', indexes=['I']), '+',
Subscript(var='M', indexes=['I', 'J'])))
assert test_parse('X * -1', Opcall('X', '*', Funcall('NEG', 1.0)))
assert test_parse('X--Y--Z', Opcall(Opcall('X', '-', Funcall('NEG', 'Y')), '-', Funcall('NEG', 'Z')))
assert test_parse('((((X))))', 'X')
return 'ok'
def run(program): execute(parse(program))
def execute(stmts):
"Parse and execute the BASIC program."
global variables, functions, column
functions, data = preprocess(stmts) # {name: function,...}, deque[number,...]
variables = defaultdict(float) # mapping of {variable: value}, default 0.0
column = 0 # column to PRINT in next
pc = 0 # program counter
ret = 0 # index (pc) that a GOSUB returns to
fors = {} # runtime map of {varname: ForState(...)}
goto = {stmt.num: i # map of {linenumber: index}
for (i, stmt) in enumerate(stmts)}
while pc < len(stmts):
(_, typ, args) = stmts[pc] # Fetch and decode the instruction
pc += 1 # Increment the program counter
if typ in ('END', 'STOP') or (typ == 'READ' and not data):
return
elif typ == 'LET':
V, exp = args
let(V, evalu(exp))
elif typ == 'READ':
for V in args[0]:
let(V, data.popleft())
elif typ == 'PRINT':
basic_print(args[0])
elif typ == 'GOTO':
pc = goto[args[0]]
elif typ == 'IF':
lhs, relational, rhs, dest = args
if functions[relational](evalu(lhs), evalu(rhs)):
pc = goto[dest]
elif typ == 'FOR':
V, start, end, step = args
variables[V] = evalu(start)
fors[V] = ForState(pc, evalu(end), evalu(step))
elif typ == 'NEXT':
V = args[0]
continu, end, step = fors[V]
if ((step >= 0 and variables[V] + step <= end) or
(step < 0 and variables[V] + step >= end)):
variables[V] += step
pc = continu
elif typ == 'GOSUB':
ret = pc
pc = goto[args[0]]
elif typ == 'RETURN':
pc = ret
import math
import random
import operator as op
def preprocess(stmts):
"""Go through stmts and return two values extracted from the declarations:
functions: a mapping of {name: function}, for both built-in and user-defined functions,
data: a queue of all the numbers in DATA statements."""
functions = { # A mapping of {name: function}; first the built-ins:
'SIN': math.sin, 'COS': math.cos, 'TAN': math.tan, 'ATN': math.atan,
'ABS': abs, 'EXP': math.exp, 'LOG': math.log, 'SQR': math.sqrt, 'INT': int,
'>': op.gt, '<': op.lt, '=': op.eq, '>=': op.ge, '<=': op.le, '<>': op.ne,
'^': pow, '+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv,
'RND': lambda _: random.random(), 'NEG': op.neg}
data = deque() # A queue of numbers that READ can read from
for (_, typ, args) in stmts:
if typ == 'DEF':
name, parm, body = args
functions[name] = Function(parm, body)
elif typ == 'DATA':
data.extend(args[0])
return functions, data
def evalu(exp):
"Evaluate an expression, returning a number."
if isinstance(exp, Opcall):
return functions[exp.op](evalu(exp.x), evalu(exp.y))
elif isinstance(exp, Funcall):
return functions[exp.f](evalu(exp.x))
elif isinstance(exp, Subscript):
return variables[exp.var, tuple(evalu(x) for x in exp.indexes)]
elif is_varname(exp):
return variables[exp]
else: # number constant
return exp
def let(V, value):
"Assign value to the variable name or Subscripted variable."
if isinstance(V, Subscript): # A subsscripted variable
variables[V.var, tuple(evalu(x) for x in V.indexes)] = value
else: # An unsubscripted variable
variables[V] = value
def basic_print(items):
"Print the items (',' / ';' / label / expression) in appropriate columns."
for item in items:
if item == ',': pad(15)
elif item == ';': pad(3)
elif is_label(item): print_string(item.replace('"', ''))
else: print_string("{:g} ".format(evalu(item)))
if (not items) or items[-1] not in (',', ';'):
newline()
def print_string(s):
"Print a string, keeping track of column, and advancing to newline if at or beyond column 75."
global column
print(s, end='')
column += len(s)
if column >= 75: newline()
def pad(width):
"Pad out to the column that is the next multiple of width."
while column % width != 0:
print_string(' ')
def newline(): global column; print(); column = 0
grammar = Grammar()
# --------------------------------------------
print('--program--')
print(program)
parse(program)
run(program)
print('\n--Linear equation solver (page 3 and 19 of the manual)--')
run('''
10 READ A1, A2, A3, A4
15 LET D = A1 * A4 - A3 * A2
20 IF D = 0 THEN 65
30 READ B1, B2
37 LET X1 = (B1*A4 - B2 * A2) / D
42 LET X2 = ( A1 * B2 - A3 * B1)/D
55 PRINT X1, X2
60 GOTO 30
65 PRINT "NO UNIQUE SOLUTION"
70 DATA 1, 2, 4
80 DATA 2, -7, 5
85 DATA 1, 3, 4, -7
90 END
''')
assert variables['D'] != 0
assert variables['X1'] == -11/3
print('\n--Find max(sin(x)) for 0 <= x <= 3 (page 25)--')
run('''
5 PRINT "X VALUE", "SINE", "RESOLUTION"
10 READ D
20 LET M = -1
30 FOR X = 0 TO 3 STEP D
40 IF SIN(X) <= M THEN 80
50 LET X0 = X
60 LET M = SIN(X)
80 NEXT X
85 PRINT X0, M, D
90 GO TO 10
95 DATA .1, .01, .001, .0001
99 END
''')
assert abs(variables['X0'] - math.pi / 2) < 0.00001
print('\n--Printing (page 32)--')
run('''
10 FOR I = 1 TO 12
20 PRINT I,
30 NEXT I
40 END''')
assert variables['I'] == 12
print('\n--# Powers (page 33)--')
run('''
5 PRINT "THIS PROGRAM COMPUTES AND PRINTS THE NTH POWERS"
6 PRINT "OF THE NUMBERS LESS THAN OR EQUAL TO N FOR VARIOUS"
7 PRINT "N FROM 1 TO 7"
8 PRINT
10 FOR N = 1 TO 7
15 PRINT "N = "; N; "I^N:"
20 FOR I = 1 TO N
30 PRINT I^N,
40 NEXT I
50 PRINT
60 PRINT
70 NEXT N
80 END''')
assert variables['N'] ** variables['I'] == 7 ** 7
print("\n--Cubes (page 35; but with STEP -2 because I haven't tested negative step yet)--")
run('''
10 FOR I = 100 TO 0 STEP -2
20 PRINT I*I*I;
30 NEXT I
40 END
''')
assert variables['I'] == 0
print('\n--Sales ledger (page 37; cleaned up a bit)--')
run('''
10 FOR I = 1 TO 3
20 READ P(I)
30 NEXT I
40 FOR I = 1 TO 3
50 FOR J = 1 TO 5
60 READ S(I, J)
70 NEXT J
80 NEXT I
90 FOR J = 1 TO 5
100 LET S = 0
110 FOR I = 1 TO 3
120 LET S = S + P(I) * S(I, J)
130 NEXT I
140 PRINT "TOTAL SALES FOR SALESMAN"J, "$"S
150 NEXT J
190 DIM S(3, 5)
200 DATA 1.25, 4.30, 2.50
210 DATA 40, 20, 37, 29, 42
220 DATA 10, 16, 3, 21, 8
230 DATA 35, 47, 29, 16, 33
300 END
''')
print(variables)
print('\n--Random number generator (page 40)--')
run('''
10 FOR I = 1 TO 100
20 PRINT INT(10 * RND(X));
30 NEXT I
40 END
''')
print('\n--DEF example: table of SIN(X) and COS(X) in degrees (page 41, expanded some)--')
run('''
5 PRINT "D"; "SIN(D)", "COS(D)", "SIN(D)^2 + COS(D)^2"
20 LET P = 3.1415926535897932 / 180
30 FOR X = 0 TO 90 STEP 15
40 PRINT X; FNS(X), FNC(X), FNS(X)^2 + FNC(X)^2
50 NEXT X
97 DEF FNS(D) = SIN(D * P)
98 DEF FNC(D) = COS(D * P)
99 END
''')
print('\n--GOSUB (page 43)--')
run('''
100 LET X = 3
110 GOSUB 400
120 PRINT U, V, W
200 LET X = 5
210 GOSUB 400
215 PRINT U, V, W
220 LET Z = U + 2*V + 3*W
230 PRINT "Z = " Z
240 STOP
400 LET U = X*X
410 LET V = X*X*X
420 LET W = X*X*X*X + X*X*X + X*X + X
430 RETURN
440 END
''')
print('\n--Sum of non-negative multiples of 0.1 less than or equal to 2, two ways (page 47)--')
run('''
5 LET S = 0
10 LET N = 0
20 LET S = S + N/10
30 IF N >= 20 THEN 60
40 LET N = N + 1
50 GOTO 20
60 PRINT S
70 END
''')
run('''
20 FOR N = 1 TO 20
40 LET S = S + N/10
50 NEXT N
60 PRINT S
70 END
''')
assert variables['S'] == sum(N/10 for N in range(1, 21))
print('\n--Checking for Syntax Errors--')
run('''
10 X = 1
20 GO TO JAIL
30 FOR I = 1
40 IF X > 0 & X < 10 GOTO 999
50 LET Z = (Z + 1
60 PRINT "OH CANADA", EH?
70 LET Z = +3
80 LET X = Y ** 2
90 LET A(I = 1
100 IF A = 0 THEN 900 + 99
110 NEXT A(I)
120 DEF F(X) = X ^ 2 + 1
130 IF X != 0 THEN 999
140 DEF FNS(X + 2*P1) = SIN(X)
150 DEF FNY(M, B) = M * X + B
160 LET 3 = X
170 LET SIN = 7 * DEADLY
180 LET X = A-1(I)
STOP
200 STOP IT, ALREADY
998 PRINT "PROGRAM STILL EXECUTES: 2 + 2 = " 2 + 2
999 END
''')
print("\n--Conway's Game of Life--")
run('''
100 REM CONWAY'S GAME OF LIFE
102 REM G IS NUMBER OF GENERATIONS, M IS MATRIX SIZE (M X M)
104 REM A(X, Y) IS 1 IFF CELL AT (X, Y) IS LIVE
106 REM L(SELF_ALIVE, NEIGHBORS_ALIVE) IS 1 IFF CELL WITH THOSE COUNTS SHOULD LIVE ON
110 LET G = 10
120 LET M = 10
125 READ A(3,4), A(3,5), A(3,6), A(6,5), A(6,6), A(7,5), A(7,6)
130 DATA 1, 1, 1, 1, 1, 1, 1
140 READ L(0, 3), L(1, 3), L(1, 2)
145 DATA 1, 1, 1
150 REM MAIN LOOP: PRINT, THEN REPEAT G TIMES: UPDATE / COPY / PRINT
155 LET I = 0
160 GOSUB 700
170 FOR I = 1 TO G
180 GOSUB 300
190 GOSUB 500
200 GOSUB 700
210 NEXT I
220 STOP
300 REM SUBROUTINE: UPDATE B = NEXT_GENERATION(A)
310 FOR Y = 1 TO M
320 FOR X = 1 TO M
325 LET N = A(X-1,Y)+A(X+1,Y)+A(X,Y-1)+A(X,Y+1)+A(X-1,Y-1)+A(X+1,Y+1)+A(X-1,Y+1)+A(X+1,Y-1)
330 LET B(X, Y) = L(A(X, Y), N)
340 NEXT X
350 NEXT Y
360 RETURN
500 REM SUBROUTINE: COPY A = B
510 FOR Y = 1 TO M
520 FOR X = 1 TO M
530 LET A(X, Y) = B(X, Y)
540 NEXT X
550 NEXT Y
560 RETURN
700 REM SUBROUTINE: PRINT A
705 PRINT " GENERATION " I
710 FOR Y = 1 TO M
720 FOR X = 1 TO M
730 IF A(X, Y) = 0 THEN 750
740 PRINT "O";
750 IF A(X, Y) = 1 THEN 770
760 PRINT ".";
770 NEXT X
780 PRINT
790 NEXT Y
795 RETURN
999 END
''')
run('''
10 PRINT "TABLE OF SQUARES"
20 LET N = 10
30 FOR V = 1 to N STEP N/5
40 PRINT V, V * V
50 NEXT V
55 PRINT V
60 END
''')
run('''
10 PRINT "TABLE OF SQUARES"
20 LET N = 10
30 FOR V = 0 TO -2
40 PRINT "WAH"
50 NEXT V
55 PRINT "WOOOOOOOOOOOH"
60 END
''')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment