Add DIM statement, clean up parser to use lvalues properly, add LValueArray
authorNick Downing <nick@ndcode.org>
Mon, 16 May 2022 16:24:43 +0000 (02:24 +1000)
committerNick Downing <nick@ndcode.org>
Mon, 16 May 2022 16:24:43 +0000 (02:24 +1000)
applesoft_basic.t
applesoft_basic.y
data_types.py
test.bas

index 15c4ce6..91cd34a 100644 (file)
@@ -71,6 +71,7 @@ class StatementHTab: Statement;
 class StatementVTab: Statement;
 class StatementGet: Statement;
 class StatementInput: Statement;
+class StatementDim: Statement;
 class RValue: Node;
 class RValueOr: RValue;
 class RValueAnd: RValue;
@@ -96,6 +97,7 @@ class RValueStrDollar: RValue;
 class RValueVal: RValue;
 class LValue: RValue;
 class LValueVariable: LValue;
+class LValueArray: LValue;
 
 %%
 
@@ -134,6 +136,7 @@ class Context:
     self,
     program = None,
     variables = None,
+    arrays = None,
     i = 0,
     j = 1,
     k = 0,
@@ -142,6 +145,7 @@ class Context:
   ):
     self.program = program if program is not None else Program()
     self.variables = variables if variables is not None else {}
+    self.arrays = arrays if arrays is not None else {}
 
     # execute pointer (line index, statement index)
     self.i = i
@@ -194,12 +198,20 @@ class Context:
     # 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.
-      )
+      null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
+      variable = Variable(null_value)
       self.variables[name] = variable
     return variable
 
+  def find_array(self, name):
+    # array is created and initialized to 11 0s if not existent
+    array = self.arrays.get(name)
+    if array is None:
+      null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
+      array = [Variable(null_value) for i in range(11)]
+      self.arrays[name] = array
+    return array
+
 def factory(tag, *args, **kwargs):
   return tag_to_class[tag](*args, **kwargs)
 
@@ -398,6 +410,29 @@ def execute(self, context):
   )
   value = apple_io.input()
   self.children[-1].set_val(context, value)
+@method(StatementDim)
+def execute(self, context):
+  dim = [
+    data_types.cint(i.get_float(context)) + 1
+    for i in self.children[1:]
+  ]
+
+  # we don't check for an existing array until here, as it could be
+  # created with the default 11 elements while evaluating dimensions
+  name = self.children[0].str_value
+  if name in context.arrays:
+    raise Exception(
+      f'?REDIM\'D ARRAY ERROR IN {context.line_number():d}'
+    )
+
+  null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
+  def create_array(i):
+    return (
+      Variable(null_value)
+    if i >= len(dim) else
+      [create_array(i + 1) for j in range(dim[i])]
+    )
+  context.arrays[name] = create_array(0)
 del execute
 
 @method(RValue)
@@ -557,6 +592,28 @@ def find_variable(self, context):
 def find_variable(self, context):
   name = self.children[0].str_value
   return context.find_variable(name)
+@method(LValueArray)
+def find_variable(self, context):
+  name = self.children[0].str_value
+  variable = context.find_array(name)
+  for i in self.children[1:]:
+    if not isinstance(variable, list):
+      # too many subscripts
+      raise Exception(
+        f'?BAD SUBSCRIPT ERROR IN {context.line_number():d}'
+      )
+    index = data_types.cint(i.get_float(context))
+    if index < 0 or index >= len(variable):
+      raise Exception(
+        f'?BAD SUBSCRIPT ERROR IN {context.line_number():d}'
+      )
+    variable = variable[index]
+  if not isinstance(variable, Variable):
+    # not enough subscripts
+    raise Exception(
+      f'?BAD SUBSCRIPT ERROR IN {context.line_number():d}'
+    )
+  return variable
 del find_variable
 
 @method(LValue)
index 0f98c9b..c205623 100644 (file)
@@ -153,43 +153,44 @@ statement_list
 
 statement_opt
   :
-  | %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.StatementLet}lvalue '=' rvalue)
+  | %space (?E{t_def.StatementLet}KEYWORD_LET lvalue '=' rvalue)
+  | %space (?E{t_def.StatementPrint, semicolon = False}KEYWORD_PRINT print_rvalue_list0)
+  | %space (?E{t_def.StatementPrint, semicolon = True}KEYWORD_PRINT print_rvalue_list1)
   | %space (?E{t_def.StatementGoto}KEYWORD_GOTO INT_LITERAL)
-  | %space (?E{t_def.StatementIf}KEYWORD_IF expression KEYWORD_THEN) statement_opt
-  | %space (?E{t_def.StatementIf}KEYWORD_IF expression KEYWORD_THEN) %space (?E{t_def.StatementGoto}INT_LITERAL)
+  | %space (?E{t_def.StatementIf}KEYWORD_IF rvalue KEYWORD_THEN) statement_opt
+  | %space (?E{t_def.StatementIf}KEYWORD_IF rvalue KEYWORD_THEN) %space (?E{t_def.StatementGoto}INT_LITERAL)
   | %space (?E{t_def.StatementEnd}KEYWORD_END)
   | %space (?E{t_def.StatementGosub}KEYWORD_GOSUB INT_LITERAL)
   | %space (?E{t_def.StatementReturn}KEYWORD_RETURN)
-  | %space (?E{t_def.StatementFor}KEYWORD_FOR VARIABLE_NAME '=' expression KEYWORD_TO expression)
-  | %space (?E{t_def.StatementFor}KEYWORD_FOR VARIABLE_NAME '=' expression KEYWORD_TO expression KEYWORD_STEP expression)
+  | %space (?E{t_def.StatementFor}KEYWORD_FOR VARIABLE_NAME '=' rvalue KEYWORD_TO rvalue)
+  | %space (?E{t_def.StatementFor}KEYWORD_FOR VARIABLE_NAME '=' rvalue KEYWORD_TO rvalue KEYWORD_STEP rvalue)
   | %space (?E{t_def.StatementNext}KEYWORD_NEXT)
   | %space (?E{t_def.StatementNext}KEYWORD_NEXT VARIABLE_NAME)
-  | %space (?E{t_def.StatementRead}KEYWORD_READ %space (?E{t_def.LValueVariable}VARIABLE_NAME) )
+  | %space (?E{t_def.StatementRead}KEYWORD_READ lvalue)
   | %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)
   | %space (?E{t_def.StatementNormal}KEYWORD_NORMAL)
   | %space (?E{t_def.StatementInverse}KEYWORD_INVERSE)
   | %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 %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) )
+  | %space (?E{t_def.StatementHTab}KEYWORD_HTAB rvalue)
+  | %space (?E{t_def.StatementVTab}KEYWORD_VTAB rvalue)
+  | %space (?E{t_def.StatementGet}KEYWORD_GET lvalue)
+  | %space (?E{t_def.StatementInput}KEYWORD_INPUT lvalue)
+  | %space (?E{t_def.StatementInput}KEYWORD_INPUT STR_LITERAL ';' lvalue)
+  | %space (?E{t_def.StatementDim}KEYWORD_DIM VARIABLE_NAME '(' rvalue_list ')')
   ;
 
-print_expression_list0
+print_rvalue_list0
   :
-  | print_expression_list0 expression
-  | print_expression_list1 expression
+  | print_rvalue_list0 rvalue
+  | print_rvalue_list1 rvalue
   ;
 
-print_expression_list1
-  : print_expression_list0 ';'
-  | print_expression_list1 ';'
+print_rvalue_list1
+  : print_rvalue_list0 ';'
+  | print_rvalue_list1 ';'
   ;
 
 data_item_list
@@ -203,31 +204,41 @@ data_item
   | STR_LITERAL
   ;
 
-expression
-  : %space (?E{t_def.RValueOr}expression KEYWORD_OR expression)
-  | %space (?E{t_def.RValueAnd}expression KEYWORD_AND expression)
-  | %space (?E{t_def.RValueLT}expression '<' expression)
-  | %space (?E{t_def.RValueEqual}expression '=' expression)
-  | %space (?E{t_def.RValueGT}expression '>' expression)
-  | %space (?E{t_def.RValueGE}expression OPERATOR_GE expression)
-  | %space (?E{t_def.RValueLE}expression OPERATOR_LE expression)
-  | %space (?E{t_def.RValueNE}expression OPERATOR_NE expression)
-  | %space (?E{t_def.RValueAdd}expression '+' expression)
-  | %space (?E{t_def.RValueSubtract}expression '-' expression)
-  | %space (?E{t_def.RValueMultiply}expression '*' expression)
-  | %space (?E{t_def.RValueDivide}expression '/' expression)
-  | %space (?E{t_def.RValuePower}expression '^' expression)
-  | %space (?E{t_def.RValueSign, sign = -1}'-' expression) %prec UNARY
-  | %space (?E{t_def.RValueSign, sign = 1}'+' expression) %prec UNARY
-  | %space (?E{t_def.RValueNot}KEYWORD_NOT expression)
-  | '(' expression ')'
-  | %space (?E{t_def.RValueIntLiteral}INT_LITERAL)
-  | %space (?E{t_def.RValueFloatLiteral}FLOAT_LITERAL)
+rvalue_list
+  : rvalue
+  | rvalue_list ',' rvalue
+  ;
+
+rvalue
+  : lvalue
+  | '(' rvalue ')'
   | %space (?E{t_def.RValueStrLiteral}STR_LITERAL)
-  | %space (?E{t_def.LValueVariable}VARIABLE_NAME)
-  | %space (?E{t_def.RValueStrDollar}KEYWORD_STR_DOLLAR '(' expression ')')
-  | %space (?E{t_def.RValueVal}KEYWORD_VAL '(' expression ')')
+  | %space (?E{t_def.RValueFloatLiteral}FLOAT_LITERAL)
+  | %space (?E{t_def.RValueIntLiteral}INT_LITERAL)
+  | %space (?E{t_def.RValueNot}KEYWORD_NOT rvalue)
+  | %space (?E{t_def.RValueSign, sign = 1}'+' rvalue) %prec UNARY
+  | %space (?E{t_def.RValueSign, sign = -1}'-' rvalue) %prec UNARY
+  | %space (?E{t_def.RValuePower}rvalue '^' rvalue)
+  | %space (?E{t_def.RValueDivide}rvalue '/' rvalue)
+  | %space (?E{t_def.RValueMultiply}rvalue '*' rvalue)
+  | %space (?E{t_def.RValueSubtract}rvalue '-' rvalue)
+  | %space (?E{t_def.RValueAdd}rvalue '+' rvalue)
+  | %space (?E{t_def.RValueNE}rvalue OPERATOR_NE rvalue)
+  | %space (?E{t_def.RValueLE}rvalue OPERATOR_LE rvalue)
+  | %space (?E{t_def.RValueGE}rvalue OPERATOR_GE rvalue)
+  | %space (?E{t_def.RValueGT}rvalue '>' rvalue)
+  | %space (?E{t_def.RValueEqual}rvalue '=' rvalue)
+  | %space (?E{t_def.RValueLT}rvalue '<' rvalue)
+  | %space (?E{t_def.RValueAnd}rvalue KEYWORD_AND rvalue)
+  | %space (?E{t_def.RValueOr}rvalue KEYWORD_OR rvalue)
+  | %space (?E{t_def.RValueStrDollar}KEYWORD_STR_DOLLAR '(' rvalue ')')
+  | %space (?E{t_def.RValueVal}KEYWORD_VAL '(' rvalue ')')
   ;
+
+lvalue
+  : %space (?E{t_def.LValueVariable}VARIABLE_NAME)
+  | %space (?E{t_def.LValueArray}VARIABLE_NAME '(' rvalue_list ')')
+  ; 
 %%
 
 def yyerror(s):
index 7983558..e1e618c 100644 (file)
@@ -1,6 +1,9 @@
 import math
 
 def cint(value):
+  # should check for illegal quantity error here?
+  # real applesoft seems to allow up to 63999, but it's not really
+  # clear under what circumstances it should be treated as unsigned
   assert isinstance(value, float)
   return ((int(math.floor(value)) + 0x8000) & 0xffff) - 0x8000
 
index 8c992df..9fb6d1d 100644 (file)
--- a/test.bas
+++ b/test.bas
 110 PRINT "K"K
 120 PRINT STR$(3.14159)
 130 PRINT VAL("3.1515926")
-140 END
+140 DIM I$(19)
+150 FOR I=0 TO 19
+160 I$(I)="ELEMENT "+STR$(I)
+170 NEXT I
+180 PRINT I$(12)
+190 J%(5)=-5.4
+200 PRINT J%(4)
+210 PRINT J%(5)
+220 PRINT J%(6)
+230 DIM AR(5,5)
+240 AR(2,2)=3
+250 AR(2,3)=14
+260 PRINT AR(2,1)AR(2,2)"."AR(2,3)
+270 END
 1000 I%=I%+1
 1010 RETURN
 2000 DATA 20,30,40.5,50,60