Created
October 2, 2019 03:48
-
-
Save scottleedavis/a8c849cfdc7f1e752147a7af0932b8ed to your computer and use it in GitHub Desktop.
an example python script that parses and runs BASIC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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