Make variables boxed for easy referencing and/or subscripting
authorNick Downing <nick@ndcode.org>
Mon, 16 May 2022 14:49:30 +0000 (00:49 +1000)
committerNick Downing <nick@ndcode.org>
Mon, 16 May 2022 14:49:30 +0000 (00:49 +1000)
apple_io.py
applesoft_basic.py
applesoft_basic.t

index f49a4ad..c2b5eec 100755 (executable)
@@ -47,6 +47,8 @@ def get():
   data = read(1) 
   if len(data) == 0:
     raise Exception('end of input') # due to piping or input redirection
+  if data == '\x7f':
+    data = '\b'
   return data
 
 def input():
index cc4c2e8..6bdeef9 100755 (executable)
@@ -47,7 +47,7 @@ with open(program_bas) as fin:
 program.post_process(program)
 
 #print('element.serialize()')
-#element.serialize(program, sys.stdout)
+element.serialize(program, sys.stdout)
 
 #print('apple_io.init()')
 apple_io.init()
index 555df61..a8e5262 100644 (file)
@@ -123,6 +123,11 @@ class ReturnException(Exception):
 class NextException(Exception):
   pass
 
+# variables are boxed for easy referencing and/or subscripting
+class Variable:
+  def __init__(self, value):
+    self.value = value
+
 class Context:
   def __init__(
     self,
@@ -184,6 +189,16 @@ class Context:
       f'?UNDEF\'D STATEMENT ERROR IN {self.line_number():d}'
     )
 
+  def find_variable(self, name):
+    # variable is created and initialized to 0 if not existent
+    variable = self.variables.get(name)
+    if variable is None:
+      variable = Variable(
+        '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
+      )
+      self.variables[name] = variable
+    return variable
+
 def factory(tag, *args, **kwargs):
   return tag_to_class[tag](*args, **kwargs)
 
@@ -221,20 +236,13 @@ def execute(self, context):
 @method(NodeStatementLet)
 def execute(self, context):
   name = self.children[0].str_value
-  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}'
-      )
+    value = self.children[1].evaluate_str(context)
   else:
-    if not isinstance(value, float):
-      raise Exception(
-        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
-      )
-    if name[-1] == '%':
+    value = self.children[1].evaluate_float(context)
+    if name[-1] == '$':
       value = data_types.cint(value)
-  context.variables[name] = value
+  context.find_variable(name).value = value
 @method(NodeStatementPrint)
 def execute(self, context):
   for i in self.children:
@@ -279,24 +287,23 @@ def execute(self, context):
 @method(NodeStatementFor)
 def execute(self, context):
   name = self.children[0].str_value
-  start = self.children[1].evaluate(context)
-  end = self.children[2].evaluate(context)
+  if name[-1] == '$':
+    raise Exception(
+      f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+    )
+  if name[-1] == '%':
+    raise Exception(
+      f'?SYNTAX ERROR IN {context.line_number():d}'
+    )
+  variable = context.find_variable(name)
+
+  variable.value = self.children[1].evaluate_float(context)
+  end = self.children[2].evaluate_float(context)
   step = (
-    self.children[3].evaluate(context)
+    self.children[3].evaluate_float(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
@@ -319,8 +326,8 @@ def execute(self, context):
         f'?NEXT WITHOUT FOR ERROR IN {context.line_number():d}'
       )
   
-    context.variables[name] += step
-    if context.variables[name] > end:
+    variable.value += step
+    if variable.value > end:
       break
 
     context.i = i
@@ -372,7 +379,7 @@ def execute(self, context):
       )
     if name[-1] == '%':
       value = data_types.cint(value)
-  context.variables[name] = value
+  context.find_variable(name).value = value
 @method(NodeStatementRestore)
 def execute(self, context):
   context.k = (
@@ -399,19 +406,11 @@ def execute(self, context):
   apple_io.flash()
 @method(NodeStatementHTab)
 def execute(self, context):
-  value = self.children[0].evaluate(context)
-  if not isinstance(value, float):
-    raise Exception(
-      f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
-    )
+  value = self.children[0].evaluate_float(context)
   apple_io.htab(data_types.cint(value))
 @method(NodeStatementVTab)
 def execute(self, context):
-  value = self.children[0].evaluate(context)
-  if not isinstance(value, float):
-    raise Exception(
-      f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
-    )
+  value = self.children[0].evaluate_float(context)
   apple_io.vtab(data_types.cint(value))
 @method(NodeStatementGet)
 def execute(self, context):
@@ -421,7 +420,7 @@ def execute(self, context):
     raise Exception(
       f'?SYNTAX ERROR IN {context.line_number():d}'
     )
-  context.variables[name] = value
+  context.find_variable(name).value = value
 @method(NodeStatementInput)
 def execute(self, context):
   apple_io._print(
@@ -435,7 +434,7 @@ def execute(self, context):
     value = data_types.val(value)
     if name[-1] == '%':
       value = data_types.cint(value)
-  context.variables[name] = value
+  context.find_variable(name).value = value
 del execute
 
 @method(NodeExpression)
@@ -443,157 +442,104 @@ def evaluate(self, context):
   raise NotImplementedError()
 @method(NodeExpressionOr)
 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}'
-    )
+  value0 = self.children[0].evaluate_float(context)
+  value1 = self.children[1].evaluate_float(context)
   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}'
-    )
+  value0 = self.children[0].evaluate_float(context)
+  value1 = self.children[1].evaluate_float(context)
   return float(value0 != 0. and value1 != 0.)
 @method(NodeExpressionLT)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   return float(value0 < value1)
 @method(NodeExpressionEqual)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   return float(value0 == value1)
 @method(NodeExpressionGT)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   return float(value0 > value1)
 @method(NodeExpressionGE)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   return float(value0 >= value1)
 @method(NodeExpressionLE)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   return float(value0 <= value1)
 @method(NodeExpressionNE)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   return float(value0 != value1)
 @method(NodeExpressionAdd)
 def evaluate(self, context):
   value0 = self.children[0].evaluate(context)
-  value1 = self.children[1].evaluate(context)
-  if (
-    (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}'
-    )
+  value1 = (
+    self.children[1].evaluate_float(context)
+  if isinstance(value0, float) else
+    self.children[1].evaluate_str(context)
+  )
   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}'
-    )
+  value0 = self.children[0].evaluate_float(context)
+  value1 = self.children[1].evaluate_float(context)
   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}'
-    )
+  value0 = self.children[0].evaluate_float(context)
+  value1 = self.children[1].evaluate_float(context)
   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}'
-    )
+  value0 = self.children[0].evaluate_float(context)
+  value1 = self.children[1].evaluate_float(context)
   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}'
-    )
+  value0 = self.children[0].evaluate_float(context)
+  value1 = self.children[1].evaluate_float(context)
   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}'
-    )
+  value = self.children[0].evaluate_float(context)
   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}'
-    )
+  value = self.children[0].evaluate_float(context)
   return float(value == 0.)
 @method(NodeExpressionIntLiteral)
 def evaluate(self, context):
@@ -607,27 +553,33 @@ def evaluate(self, context):
 @method(NodeExpressionVariable)
 def evaluate(self, context):
   name = self.children[0].str_value
-  if name[-1] == '$':
-    value = context.variables.get(name, '')
-  elif name[-1] == '%':
-    value = float(context.variables.get(name, 0))
-  else:
-    value = context.variables.get(name, 0.)
-  return value
+  return context.find_variable(name).value
 @method(NodeExpressionStrDollar)
 def evaluate(self, context):
-  value = self.children[0].evaluate(context)
+  value = self.children[0].evaluate_float(context)
+  return data_types.str_dollar(value)
+@method(NodeExpressionVal)
+def evaluate(self, context):
+  value = self.children[0].evaluate_str(context)
+  return data_types.val(value)
+del evaluate
+
+@method(NodeExpression)
+def evaluate_float(self, context):
+  value = self.evaluate(context)
   if not isinstance(value, float):
     raise Exception(
       f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
     )
-  return data_types.str_dollar(value)
-@method(NodeExpressionVal)
-def evaluate(self, context):
-  value = self.children[0].evaluate(context)
+  return value
+del evaluate_float
+
+@method(NodeExpression)
+def evaluate_str(self, context):
+  value = self.evaluate(context)
   if not isinstance(value, str):
     raise Exception(
       f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
     )
-  return data_types.val(value)
-del evaluate
+  return value
+del evaluate_str