class NodeStatementEnd: NodeStatement;
class NodeStatementGosub: NodeStatement;
class NodeStatementReturn: NodeStatement;
+class NodeStatementFor: NodeStatement;
+class NodeStatementNext: NodeStatement;
class NodeExpression: Node;
class NodeExpressionOr: NodeExpression;
class NodeExpressionAnd: NodeExpression;
class ReturnException(Exception):
pass
+class NextException(Exception):
+ pass
+
class Context:
def __init__(self, program = None, variables = None, i = 0, j = 1):
self.program = program if program is not None else NodeProgram()
raise Exception(
f'?RETURN WITHOUT GOSUB ERROR IN {self.line_number():d}'
)
+ except NextException:
+ raise Exception(
+ f'?NEXT WITHOUT FOR ERROR IN {self.line_number():d}'
+ )
def line_number(self):
assert self.i < len(self.program.children)
self.j = 1
break
else:
- raise Exception(f'?UNDEF\'D STATEMENT ERROR IN {self.line_number():d}')
+ raise Exception(
+ f'?UNDEF\'D STATEMENT ERROR IN {self.line_number():d}'
+ )
def factory(tag, *args, **kwargs):
return tag_to_class[tag](*args, **kwargs)
value = self.children[1].evaluate(context)
if name[-1] == '$':
if not isinstance(value, str):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
elif name[-1] == '%':
if not isinstance(value, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
value = ((int(math.floor(value)) + 0x8000) & 0xffff) - 0x8000
else:
if not isinstance(value, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
context.variables[name] = value
@method(NodeStatementPrint)
def execute(self, context):
exponent = ''
else:
if exponent >= 100:
- raise Exception(f'?OVERFLOW ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?OVERFLOW ERROR IN {context.line_number():d}'
+ )
value *= 10. ** (8 - exponent)
int_value = int(round(value))
value /= 10.
exponent += 1
if exponent >= 100:
- raise Exception(f'?OVERFLOW ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?OVERFLOW ERROR IN {context.line_number():d}'
+ )
int_value = int(round(value))
assert int_value >= 100000000 and int_value < 1000000000
context.execute()
except ReturnException:
pass
+ except NextException:
+ raise Exception(
+ f'?NEXT WITHOUT FOR ERROR IN {context.line_number():d}'
+ )
context.i = i
context.j = j
@method(NodeStatementReturn)
def execute(self, context):
raise ReturnException()
+@method(NodeStatementFor)
+def execute(self, context):
+ name = self.children[0].str_value
+ start = self.children[1].evaluate(context)
+ end = self.children[2].evaluate(context)
+ step = (
+ self.children[3].evaluate(context)
+ if len(self.children) >= 4 else
+ 1.
+ )
+ if (
+ name[-1] in '$%' or
+ not isinstance(start, float) or
+ not isinstance(end, float) or
+ not isinstance(step, float)
+ ):
+ # interestingly, real applesoft treats % (but not $) as syntax error
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
+ context.variables[name] = start
+
+ i = context.i
+ j = context.j
+ while True:
+ try:
+ context.execute()
+ except NextException:
+ pass
+
+ assert context.i < len(context.program.children)
+ line = context.program.children[context.i]
+ assert context.j > 1 and context.j <= len(line.children)
+ statement = line.children[context.j - 1]
+ assert isinstance(statement, NodeStatementNext)
+ if (
+ len(statement.children) >= 1 and
+ statement.children[0].str_value != name
+ ):
+ raise Exception(
+ f'?NEXT WITHOUT FOR ERROR IN {context.line_number():d}'
+ )
+
+ context.variables[name] += step
+ if context.variables[name] > end:
+ break
+
+ context.i = i
+ context.j = j
+@method(NodeStatementNext)
+def execute(self, context):
+ raise NextException()
del execute
@method(NodeExpression)
value0 = self.children[0].evaluate(context)
value1 = self.children[1].evaluate(context)
if not isinstance(value0, float) or not isinstance(value1, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 != 0. or value1 != 0.)
@method(NodeExpressionAnd)
def evaluate(self, context):
value0 = self.children[0].evaluate(context)
value1 = self.children[1].evaluate(context)
if not isinstance(value0, float) or not isinstance(value1, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 != 0. and value1 != 0.)
@method(NodeExpressionLT)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 < value1)
@method(NodeExpressionEqual)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 == value1)
@method(NodeExpressionGT)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 > value1)
@method(NodeExpressionGE)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 >= value1)
@method(NodeExpressionLE)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 <= value1)
@method(NodeExpressionNE)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value0 != value1)
@method(NodeExpressionAdd)
def evaluate(self, context):
(not isinstance(value0, str) or not isinstance(value1, str)) and
(not isinstance(value0, float) or not isinstance(value1, float))
):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return value0 + value1
@method(NodeExpressionSubtract)
def evaluate(self, context):
value0 = self.children[0].evaluate(context)
value1 = self.children[1].evaluate(context)
if not isinstance(value0, float) or not isinstance(value1, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return value0 - value1
@method(NodeExpressionMultiply)
def evaluate(self, context):
value0 = self.children[0].evaluate(context)
value1 = self.children[1].evaluate(context)
if not isinstance(value0, float) or not isinstance(value1, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return value0 * value1
@method(NodeExpressionDivide)
def evaluate(self, context):
value0 = self.children[0].evaluate(context)
value1 = self.children[1].evaluate(context)
if not isinstance(value0, float) or not isinstance(value1, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return value0 / value1
@method(NodeExpressionPower)
def evaluate(self, context):
value0 = self.children[0].evaluate(context)
value1 = self.children[1].evaluate(context)
if not isinstance(value0, float) or not isinstance(value1, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return value0 ** value1
@method(NodeExpressionSign)
def evaluate(self, context):
value = self.children[0].evaluate(context)
if not isinstance(value, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return self.sign * value
@method(NodeExpressionNot)
def evaluate(self, context):
value = self.children[0].evaluate(context)
if not isinstance(value, float):
- raise Exception(f'?TYPE MISMATCH ERROR IN {context.line_number():d}')
+ raise Exception(
+ f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+ )
return float(value == 0.)
@method(NodeExpressionIntLiteral)
def evaluate(self, context):