Implement GOSUB and RETURN
authorNick Downing <nick@ndcode.org>
Sun, 15 May 2022 14:47:50 +0000 (00:47 +1000)
committerNick Downing <nick@ndcode.org>
Sun, 15 May 2022 14:47:50 +0000 (00:47 +1000)
applesoft_basic.l
applesoft_basic.t
applesoft_basic.y
test.bas

index 0402224..6516eb8 100644 (file)
@@ -243,8 +243,8 @@ G\ *R/(\ *[A-Z0-9])*(\ *[$%])? {
 G\ *O\ *T\ *O/(\ *[A-Z0-9])*(\ *[$%])? {
   return y_tab.KEYWORD_GOTO
 }
-G\ *O/(\ *[A-Z0-9])*(\ *[$%])? {
-  return y_tab.KEYWORD_GO
+G\ *O\ *S\ *U\ *B/(\ *[A-Z0-9])*(\ *[$%])? {
+  return y_tab.KEYWORD_GOSUB
 }
 G\ *E\ *T/(\ *[A-Z0-9])*(\ *[$%])? {
   return y_tab.KEYWORD_GET
index cf1bc9f..8d17e2a 100644 (file)
@@ -55,6 +55,8 @@ class NodeStatementPrint: NodeStatement {
 class NodeStatementGoto: NodeStatement;
 class NodeStatementIf: NodeStatement;
 class NodeStatementEnd: NodeStatement;
+class NodeStatementGosub: NodeStatement;
+class NodeStatementReturn: NodeStatement;
 class NodeExpression: Node;
 class NodeExpressionOr: NodeExpression;
 class NodeExpressionAnd: NodeExpression;
@@ -99,6 +101,9 @@ class NodeExpressionVariable: NodeExpression;
 class EndException(Exception):
   pass
 
+class ReturnException(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()
@@ -111,6 +116,10 @@ class Context:
       self.execute()
     except EndException:
       pass
+    except ReturnException:
+      raise Exception(
+        f'?RETURN WITHOUT GOSUB ERROR IN {self.line_number():d}'
+      )
 
   def line_number(self):
     assert self.i < len(self.program.children)
@@ -126,6 +135,16 @@ class Context:
       else:
         self.i += 1
         self.j = 1
+    raise EndException()
+
+  def goto(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}')
 
 def factory(tag, *args, **kwargs):
   return tag_to_class[tag](*args, **kwargs)
@@ -236,14 +255,7 @@ def execute(self, context):
     print()
 @method(NodeStatementGoto)
 def execute(self, context):
-  target = self.children[0].int_value
-  for i in range(len(context.program.children)):
-    if target == context.program.children[i].children[0].int_value:
-      context.i = i
-      context.j = 1
-      break
-  else:
-    raise Exception(f'?UNDEF\'D STATEMENT ERROR IN {context.line_number():d}')
+  context.goto(self.children[0].int_value)
 @method(NodeStatementIf)
 def execute(self, context):
   value = self.children[0].evaluate(context)
@@ -253,6 +265,20 @@ def execute(self, context):
 @method(NodeStatementEnd)
 def execute(self, context):
   raise EndException()
+@method(NodeStatementGosub)
+def execute(self, context):
+  i = context.i
+  j = context.j
+  context.goto(self.children[0].int_value)
+  try:
+    context.execute()
+  except ReturnException:
+    pass
+  context.i = i
+  context.j = j
+@method(NodeStatementReturn)
+def execute(self, context):
+  raise ReturnException()
 del execute
 
 @method(NodeExpression)
index 001ca50..42c9039 100644 (file)
@@ -46,7 +46,7 @@
 %token KEYWORD_FOR
 %token KEYWORD_FRE
 %token KEYWORD_GET
-%token KEYWORD_GO
+%token KEYWORD_GOSUB
 %token KEYWORD_GOTO
 %token KEYWORD_GR
 %token KEYWORD_HCOLOR_EQUAL
@@ -161,6 +161,8 @@ statement_opt
   | %space (?E{t_def.NodeStatementIf}KEYWORD_IF expression KEYWORD_THEN) statement_opt
   | %space (?E{t_def.NodeStatementIf}KEYWORD_IF expression KEYWORD_THEN) %space (?E{t_def.NodeStatementGoto}INT_LITERAL)
   | %space (?E{t_def.NodeStatementEnd}KEYWORD_END)
+  | %space (?E{t_def.NodeStatementGosub}KEYWORD_GOSUB INT_LITERAL)
+  | %space (?E{t_def.NodeStatementReturn}KEYWORD_RETURN)
   ;
 
 print_expression_list0
index 0532477..07ee00e 100644 (file)
--- a/test.bas
+++ b/test.bas
@@ -1,5 +1,7 @@
 10 I$="PI":I%=-3.141:I=3.141
 20 PRINT I$" "I%" "I
-30 I%=I%+1
+30 GOSUB 100
 40 IF I%>5 THEN END
 50 GOTO 20
+100 I%=I%+1
+110 RETURN