Implement READ, RESTORE and DATA, but no empty items or unquoted strings yet
authorNick Downing <nick@ndcode.org>
Sun, 15 May 2022 16:05:22 +0000 (02:05 +1000)
committerNick Downing <nick@ndcode.org>
Sun, 15 May 2022 16:05:22 +0000 (02:05 +1000)
applesoft_basic.t
applesoft_basic.y
test.bas

index 84c8db1..dcb0a0f 100644 (file)
@@ -59,6 +59,9 @@ class NodeStatementGosub: NodeStatement;
 class NodeStatementReturn: NodeStatement;
 class NodeStatementFor: NodeStatement;
 class NodeStatementNext: NodeStatement;
+class NodeStatementRead: NodeStatement;
+class NodeStatementRestore: NodeStatement;
+class NodeStatementData: NodeStatement;
 class NodeExpression: Node;
 class NodeExpressionOr: NodeExpression;
 class NodeExpressionAnd: NodeExpression;
@@ -110,12 +113,28 @@ class NextException(Exception):
   pass
 
 class Context:
-  def __init__(self, program = None, variables = None, i = 0, j = 1):
+  def __init__(
+    self,
+    program = None,
+    variables = None,
+    i = 0,
+    j = 1,
+    k = 0,
+    l = 1,
+    m = 0
+  ):
     self.program = program if program is not None else NodeProgram()
     self.variables = variables if variables is not None else {}
+
+    # execute pointer (line index, statement index)
     self.i = i
     self.j = j
 
+    # read pointer (line index, statement index, item index)
+    self.k = k
+    self.l = l
+    self.m = m
+
   def run(self):
     try:
       self.execute()
@@ -146,16 +165,13 @@ class Context:
         self.j = 1
     raise EndException()
 
-  def goto(self, target):
+  def find_line(self, target):
     for i in range(len(self.program.children)):
       if target == self.program.children[i].children[0].int_value:
-        self.i = i
-        self.j = 1
-        break
-    else:
-      raise Exception(
-        f'?UNDEF\'D STATEMENT ERROR IN {self.line_number():d}'
-      )
+        return i
+    raise Exception(
+      f'?UNDEF\'D STATEMENT ERROR IN {self.line_number():d}'
+    )
 
 def factory(tag, *args, **kwargs):
   return tag_to_class[tag](*args, **kwargs)
@@ -276,7 +292,8 @@ def execute(self, context):
     print()
 @method(NodeStatementGoto)
 def execute(self, context):
-  context.goto(self.children[0].int_value)
+  context.i = context.find_line(self.children[0].int_value)
+  context.j = 1
 @method(NodeStatementIf)
 def execute(self, context):
   value = self.children[0].evaluate(context)
@@ -290,7 +307,8 @@ def execute(self, context):
 def execute(self, context):
   i = context.i
   j = context.j
-  context.goto(self.children[0].int_value)
+  context.i = context.find_line(self.children[0].int_value)
+  context.j = 1
   try:
     context.execute()
   except ReturnException:
@@ -356,6 +374,71 @@ def execute(self, context):
 @method(NodeStatementNext)
 def execute(self, context):
   raise NextException()
+@method(NodeStatementRead)
+def execute(self, context):
+  while context.k < len(context.program.children):
+    line = context.program.children[context.k]
+    while context.l < len(line.children):
+      statement = line.children[context.l]
+      if (
+        isinstance(statement, NodeStatementData) and
+          context.m < len(statement.children)
+      ):
+        item = statement.children[context.m]
+        context.m += 1
+        break
+      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}'
+    )
+
+  name = self.children[0].str_value
+  if name[-1] == '$':
+    if not isinstance(item, NodeStrLiteral):
+      raise Exception(
+        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+      )
+    value = item.text[0]
+  elif name[-1] == '%':
+    if isinstance(item, NodeTextIntLiteral):
+      value = item.int_value
+    elif isinstance(item, NodeFloatLiteral):
+      value = int(math.floor(item.float_value))
+    else:
+      raise Exception(
+        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+      )
+    value = ((value + 0x8000) & 0xffff) - 0x8000
+  else:
+    if isinstance(item, NodeTextIntLiteral):
+      value = float(item.int_value)
+    elif isinstance(item, NodeFloatLiteral):
+      value = item.float_value
+    else:
+      raise Exception(
+        f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
+      )
+  context.variables[name] = value
+@method(NodeStatementRestore)
+def execute(self, context):
+  context.k = (
+    context.find_line(self.children[0].int_value)
+  if len(self.children) >= 1 else
+    0
+  )
+  context.l = 1
+  context.m = 0
+@method(NodeStatementData)
+def execute(self, context):
+  pass
 del execute
 
 @method(NodeExpression)
index e059706..c052194 100644 (file)
@@ -167,6 +167,9 @@ statement_opt
   | %space (?E{t_def.NodeStatementFor}KEYWORD_FOR VARIABLE '=' expression KEYWORD_TO expression KEYWORD_STEP expression)
   | %space (?E{t_def.NodeStatementNext}KEYWORD_NEXT)
   | %space (?E{t_def.NodeStatementNext}KEYWORD_NEXT VARIABLE)
+  | %space (?E{t_def.NodeStatementRead}KEYWORD_READ VARIABLE)
+  | %space (?E{t_def.NodeStatementRestore}KEYWORD_RESTORE INT_LITERAL)
+  | %space (?E{t_def.NodeStatementData}KEYWORD_DATA data_item_list)
   ;
 
 print_expression_list0
@@ -180,6 +183,17 @@ print_expression_list1
   | print_expression_list1 ';'
   ;
 
+data_item_list
+  : data_item
+  | data_item_list ',' data_item
+  ;
+
+data_item
+  : INT_LITERAL
+  | FLOAT_LITERAL
+  | STR_LITERAL
+  ;
+
 expression
   : %space (?E{t_def.NodeExpressionOr}expression KEYWORD_OR expression)
   | %space (?E{t_def.NodeExpressionAnd}expression KEYWORD_AND expression)
index 08d2ecc..b1b4762 100644 (file)
--- a/test.bas
+++ b/test.bas
@@ -1,11 +1,17 @@
 10 I$="PI":I%=-3.141:I=3.141
 20 PRINT I$" "I%" "I
-30 GOSUB 100
+30 GOSUB 1000
 40 IF I%>5 THEN 60
 50 GOTO 20
 60 FOR J=1 TO 10
-70 PRINT "J"J
+70 READ K
+70 PRINT "J"J" K"K
 80 NEXT J
-90 END
-100 I%=I%+1
-110 RETURN
+90 RESTORE 2010
+100 READ K
+110 PRINT "K"K
+120 END
+1000 I%=I%+1
+1010 RETURN
+2000 DATA 20,30,40.5,50,60
+2010 DATA 70,80,90,100,110,120