Add LValue node, so that locating the variable for get or set uses common code
authorNick Downing <nick@ndcode.org>
Mon, 16 May 2022 15:52:09 +0000 (01:52 +1000)
committerNick Downing <nick@ndcode.org>
Mon, 16 May 2022 15:52:09 +0000 (01:52 +1000)
applesoft_basic.t
applesoft_basic.y
data_types.py

index 17beef1..15c4ce6 100644 (file)
@@ -92,9 +92,10 @@ class RValueNot: RValue;
 class RValueIntLiteral: RValue;
 class RValueFloatLiteral: RValue;
 class RValueStrLiteral: RValue;
-class RValueVariable: RValue;
 class RValueStrDollar: RValue;
 class RValueVal: RValue;
+class LValue: RValue;
+class LValueVariable: LValue;
 
 %%
 
@@ -235,14 +236,8 @@ def execute(self, context):
   raise NotImplementedError()
 @method(StatementLet)
 def execute(self, context):
-  name = self.children[0].str_value
-  if name[-1] == '$':
-    value = self.children[1].get_str(context)
-  else:
-    value = self.children[1].get_float(context)
-    if name[-1] == '$':
-      value = data_types.cint(value)
-  context.find_variable(name).value = value
+  value = self.children[1].get(context)
+  self.children[0].set(context, value)
 @method(StatementPrint)
 def execute(self, context):
   for i in self.children:
@@ -287,14 +282,12 @@ def execute(self, context):
 @method(StatementFor)
 def execute(self, context):
   name = self.children[0].str_value
-  if name[-1] == '$':
+  # note: if the loop index is an integer, we will print a type
+  # mismatch error, whereas applesoft would print a syntax error
+  if name[-1] in '$%':
     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].get_float(context)
@@ -346,40 +339,18 @@ def execute(self, context):
           context.m < len(statement.children)
       ):
         item = statement.children[context.m]
+        self.children[0].set_val(context, element.to_text(item))
         context.m += 1
-        break
+        return
       context.l += 1
       context.m = 0
-    else:
-      context.k += 1
-      context.l = 1
-      assert context.m == 0
-      continue
-    break
-  else:
-    raise Exception(
-      f'?OUT OF DATA ERROR IN {context.line_number():d}'
-    )
+    context.k += 1
+    context.l = 1
+    assert context.m == 0
+  raise Exception(
+    f'?OUT OF DATA ERROR IN {context.line_number():d}'
+  )
 
-  name = self.children[0].str_value
-  if name[-1] == '$':
-    if not isinstance(item, StrLiteral):
-      raise Exception(
-        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
-      )
-    value = item.text[0]
-  else:
-    if isinstance(item, IntLiteral):
-      value = float(item.int_value)
-    elif isinstance(item, FloatLiteral):
-      value = item.float_value
-    else:
-      raise Exception(
-        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
-      )
-    if name[-1] == '%':
-      value = data_types.cint(value)
-  context.find_variable(name).value = value
 @method(StatementRestore)
 def execute(self, context):
   context.k = (
@@ -414,13 +385,10 @@ def execute(self, context):
   apple_io.vtab(data_types.cint(value))
 @method(StatementGet)
 def execute(self, context):
-  name = self.children[0].str_value
   value = apple_io.get()
-  if name[-1] != '$':
-    raise Exception(
-      f'?SYNTAX ERROR IN {context.line_number():d}'
-    )
-  context.find_variable(name).value = value
+  # note: if the lvalue isn't a string then we will print a type
+  # mismatch error, whereas applesoft would print a syntax error
+  self.children[0].set(context, value)
 @method(StatementInput)
 def execute(self, context):
   apple_io._print(
@@ -428,13 +396,8 @@ def execute(self, context):
   if len(self.children) >= 2 else
     '?'
   )
-  name = self.children[-1].str_value
   value = apple_io.input()
-  if name[-1] != '$':
-    value = data_types.val(value)
-    if name[-1] == '%':
-      value = data_types.cint(value)
-  context.find_variable(name).value = value
+  self.children[-1].set_val(context, value)
 del execute
 
 @method(RValue)
@@ -550,10 +513,6 @@ def get(self, context):
 @method(RValueStrLiteral)
 def get(self, context):
   return self.children[0].text[0]
-@method(RValueVariable)
-def get(self, context):
-  name = self.children[0].str_value
-  return context.find_variable(name).value
 @method(RValueStrDollar)
 def get(self, context):
   value = self.children[0].get_float(context)
@@ -562,6 +521,13 @@ def get(self, context):
 def get(self, context):
   value = self.children[0].get_str(context)
   return data_types.val(value)
+@method(LValue)
+def get(self, context):
+  value = self.find_variable(context).value
+  name = self.children[0].str_value
+  if name[-1] == '%':
+    value = float(value)
+  return value
 del get
 
 @method(RValue)
@@ -583,3 +549,41 @@ def get_str(self, context):
     )
   return value
 del get_str
+
+@method(LValue)
+def find_variable(self, context):
+  raise NotImplementedError()
+@method(LValueVariable)
+def find_variable(self, context):
+  name = self.children[0].str_value
+  return context.find_variable(name)
+del find_variable
+
+@method(LValue)
+def set(self, context, value):
+  name = self.children[0].str_value
+  if name[-1] == '$':
+    if not isinstance(value, str):
+      raise Exception(
+        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+      )
+  else:
+    if not isinstance(value, float):
+      raise Exception(
+        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+      )
+    if name[-1] == '%':
+      value = data_types.cint(value)
+  self.find_variable(context).value = value
+del set
+
+@method(LValue)
+def set_val(self, context, value):
+  assert isinstance(value, str)
+  name = self.children[0].str_value
+  if name[-1] != '$':
+    value = data_types.val(value)
+    if name[-1] == '%':
+      value = data_types.cint(value)
+  self.find_variable(context).value = value
+del set_val
index 81cf6ba..0f98c9b 100644 (file)
@@ -153,8 +153,8 @@ statement_list
 
 statement_opt
   :
-  | %space (?E{t_def.StatementLet}VARIABLE_NAME '=' expression)
-  | %space (?E{t_def.StatementLet}KEYWORD_LET VARIABLE_NAME '=' expression)
+  | %space (?E{t_def.StatementLet}(?E{t_def.LValueVariable}VARIABLE_NAME) '=' expression)
+  | %space (?E{t_def.StatementLet}KEYWORD_LET %space (?E{t_def.LValueVariable}VARIABLE_NAME) '=' expression)
   | %space (?E{t_def.StatementPrint, semicolon = False}KEYWORD_PRINT print_expression_list0)
   | %space (?E{t_def.StatementPrint, semicolon = True}KEYWORD_PRINT print_expression_list1)
   | %space (?E{t_def.StatementGoto}KEYWORD_GOTO INT_LITERAL)
@@ -167,7 +167,7 @@ statement_opt
   | %space (?E{t_def.StatementFor}KEYWORD_FOR VARIABLE_NAME '=' expression KEYWORD_TO expression KEYWORD_STEP expression)
   | %space (?E{t_def.StatementNext}KEYWORD_NEXT)
   | %space (?E{t_def.StatementNext}KEYWORD_NEXT VARIABLE_NAME)
-  | %space (?E{t_def.StatementRead}KEYWORD_READ VARIABLE_NAME)
+  | %space (?E{t_def.StatementRead}KEYWORD_READ %space (?E{t_def.LValueVariable}VARIABLE_NAME) )
   | %space (?E{t_def.StatementRestore}KEYWORD_RESTORE INT_LITERAL)
   | %space (?E{t_def.StatementData}KEYWORD_DATA data_item_list)
   | %space (?E{t_def.StatementHome}KEYWORD_HOME)
@@ -176,9 +176,9 @@ statement_opt
   | %space (?E{t_def.StatementFlash}KEYWORD_FLASH)
   | %space (?E{t_def.StatementHTab}KEYWORD_HTAB expression)
   | %space (?E{t_def.StatementVTab}KEYWORD_VTAB expression)
-  | %space (?E{t_def.StatementGet}KEYWORD_GET VARIABLE_NAME)
-  | %space (?E{t_def.StatementInput}KEYWORD_INPUT VARIABLE_NAME)
-  | %space (?E{t_def.StatementInput}KEYWORD_INPUT STR_LITERAL ';' VARIABLE_NAME)
+  | %space (?E{t_def.StatementGet}KEYWORD_GET %space (?E{t_def.LValueVariable}VARIABLE_NAME) )
+  | %space (?E{t_def.StatementInput}KEYWORD_INPUT %space (?E{t_def.LValueVariable}VARIABLE_NAME) )
+  | %space (?E{t_def.StatementInput}KEYWORD_INPUT STR_LITERAL ';' %space (?E{t_def.LValueVariable}VARIABLE_NAME) )
   ;
 
 print_expression_list0
@@ -224,7 +224,7 @@ expression
   | %space (?E{t_def.RValueIntLiteral}INT_LITERAL)
   | %space (?E{t_def.RValueFloatLiteral}FLOAT_LITERAL)
   | %space (?E{t_def.RValueStrLiteral}STR_LITERAL)
-  | %space (?E{t_def.RValueVariable}VARIABLE_NAME)
+  | %space (?E{t_def.LValueVariable}VARIABLE_NAME)
   | %space (?E{t_def.RValueStrDollar}KEYWORD_STR_DOLLAR '(' expression ')')
   | %space (?E{t_def.RValueVal}KEYWORD_VAL '(' expression ')')
   ;
index 09f637a..7983558 100644 (file)
@@ -64,4 +64,5 @@ def str_dollar(value):
 def val(value):
   # make this more sophisticated later
   assert isinstance(value, str)
-  return float(value)
+  value = value.replace(' ', '')
+  return float(value) if len(value) else 0.