Implement all functions except some I/O related ones
authorNick Downing <nick@ndcode.org>
Tue, 17 May 2022 13:02:59 +0000 (23:02 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 17 May 2022 14:11:26 +0000 (00:11 +1000)
apple_io.py
applesoft_basic.t
applesoft_basic.y
data_types.py
test.bas

index 66d28fa..a2a9274 100755 (executable)
@@ -288,9 +288,17 @@ def hlin(x0, x1, y):
 def vlin(y0, y1, x):
   pass
 
-def tab(x):
-  x -= 1 + mem[ZP_CH]
-  return ' ' * x if x >= 1 else ''
+def usr(n):
+  return 0
+
+def scrn(x, y):
+  return 0
+
+def pdl(n):
+  return 127
+
+def pos():
+  return mem[ZP_CH]
 
 if __name__ == '__main__':
   init()
index d7e58ca..beb5de0 100644 (file)
   import apple_io
   import data_types
   import element
+  import gc
+  import math
+  import random
+  import time
+
+  # globals
+  random.seed(time.time())
+  last_random = random.random()
 %}
 
 %%
@@ -388,10 +396,7 @@ def execute(self, context):
 @method(StatementDim)
 def execute(self, context):
   for i in self.children:
-    dim = [
-      data_types.cint(j.get_float(context)) + 1
-      for j in i.children[1:]
-    ]
+    dim = [j.get_int(context) + 1 for j in i.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
@@ -446,33 +451,25 @@ def execute(self, context):
   assert False
 @method(StatementCall)
 def execute(self, context):
-  value = self.children[0].get_float()
-  apple_io.call(data_types.cint(value))
+  value = self.children[0].get_int(context)
+  apple_io.call(value)
 @method(StatementPlot)
 def execute(self, context):
-  value0 = self.children[0].get_float()
-  value1 = self.children[1].get_float()
-  apple_io.plot(data_types.cint(value0), data_types.cint(value1))
+  value0 = self.children[0].get_int(context)
+  value1 = self.children[1].get_int(context)
+  apple_io.plot(value0, value1)
 @method(StatementHLin)
 def execute(self, context):
-  value0 = self.children[0].get_float()
-  value1 = self.children[1].get_float()
-  value2 = self.children[2].get_float()
-  apple_io.hlin(
-    data_types.cint(value0),
-    data_types.cint(value1),
-    data_types.cint(value2)
-  )
+  value0 = self.children[0].get_int(context)
+  value1 = self.children[1].get_int(context)
+  value2 = self.children[2].get_int(context)
+  apple_io.hlin(value0, value1, value2)
 @method(StatementVLin)
 def execute(self, context):
-  value0 = self.children[0].get_float()
-  value1 = self.children[1].get_float()
-  value2 = self.children[2].get_float()
-  apple_io.vlin(
-    data_types.cint(value0),
-    data_types.cint(value1),
-    data_types.cint(value2)
-  )
+  value0 = self.children[0].get_int(context)
+  value1 = self.children[1].get_int(context)
+  value2 = self.children[2].get_int(context)
+  apple_io.vlin(value0, value1, value2)
 @method(StatementHGR2)
 def execute(self, context):
   assert False
@@ -493,8 +490,8 @@ def execute(self, context):
   assert False
 @method(StatementHTab)
 def execute(self, context):
-  value = self.children[0].get_float(context)
-  apple_io.htab(data_types.cint(value))
+  value = self.children[0].get_int(context)
+  apple_io.htab(value)
 @method(StatementHome)
 def execute(self, context):
   apple_io.home()
@@ -524,15 +521,15 @@ def execute(self, context):
   apple_io.flash()
 @method(StatementColorEqual)
 def execute(self, context):
-  value = self.children[0].get_float()
-  apple_io.color(data_types.cint(value))
+  value = self.children[0].get_int(context)
+  apple_io.color(value)
 @method(StatementPop)
 def execute(self, context):
   assert False
 @method(StatementVTab)
 def execute(self, context):
-  value = self.children[0].get_float(context)
-  apple_io.vtab(data_types.cint(value))
+  value = self.children[0].get_int(context)
+  apple_io.vtab(value)
 @method(StatementHimemColon)
 def execute(self, context):
   assert False
@@ -628,9 +625,9 @@ def execute(self, context):
   assert False
 @method(StatementPoke)
 def execute(self, context):
-  value0 = self.children[0].get_float()
-  value1 = self.children[1].get_float()
-  apple_io.poke(data_types.cint(value0), data_types.cint(value1))
+  value0 = self.children[0].get_int(context)
+  value1 = self.children[1].get_int(context)
+  apple_io.poke(value0, value1)
 @method(StatementPrint)
 def execute(self, context):
   for i in self.children:
@@ -674,12 +671,18 @@ def get(self, context):
   return float(self.children[0].int_value)
 @method(RValueTabLParen)
 def get(self, context):
-  value = self.children[0].get_float(context)
-  return apple_io.tab(data_types.cint(value))
+  value = self.children[0].get_int(context)
+  # strangely, pos() is 0-based whereas tab() is 1-based
+  value -= 1 + apple_io.pos()
+  return  ' ' * value if value >= 1 else ''
 @method(RValueSpcLParen)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_int(context)
+  if value < 0:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return ' ' * value 
 @method(RValueNot)
 def get(self, context):
   value = self.children[0].get_float(context)
@@ -783,76 +786,91 @@ def get(self, context):
   return float(value0 != value1)
 @method(RValueSgn)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return -1. if value < 0. else 1. if value > 0. else 0.
 @method(RValueInt)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return float(math.floor(value))
 @method(RValueAbs)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return abs(value)
 @method(RValueUsr)
 def get(self, context):
   value = self.children[0].get(context)
-  assert False
+  value = apple_io.usr(value)
+  if isinstance(value, int):
+    value = float(value)
+  return value
 @method(RValueFre)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  gc.collect()
+  return -29188. # value I get from a freshly booted system
 @method(RValueScrnLParen)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value0 = self.children[0].get_int(context)
+  value1 = self.children[1].get_int(context)
+  return float(apple_io.scrn(value0, value1))
 @method(RValuePdl)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_int(context)
+  return float(apple_io.pdl(value))
 @method(RValuePos)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  return float(apple_io.pos())
 @method(RValueSqr)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  if value < 0.:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return math.sqrt(value)
 @method(RValueRnd)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  if value:
+    if value < 0.:
+      random.seed(value)
+    last_random = random.random()
+  return last_random
 @method(RValueLog)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  if value <= 0.:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return math.log(value)
 @method(RValueExp)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return math.exp(value)
 @method(RValueCos)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return math.cos(value)
 @method(RValueSin)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return math.sin(value)
 @method(RValueTan)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return math.tan(value)
 @method(RValueAtn)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_float(context)
+  return math.atan(value)
 @method(RValuePeek)
 def get(self, context):
-  value = self.children[0].get_str(context)
-  return float(apple_io.peek(data_types.cint(value)))
+  value = self.children[0].get_int(context)
+  return float(apple_io.peek(value))
 @method(RValueLen)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_str(context)
+  return float(len(value))
 @method(RValueStrDollar)
 def get(self, context):
   value = self.children[0].get_float(context)
@@ -863,24 +881,55 @@ def get(self, context):
   return data_types.val(value)
 @method(RValueAsc)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_str(context)
+  if len(value) == 0:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return float(ord(value[0]))
 @method(RValueChrDollar)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value = self.children[0].get_int(context)
+  if value < 0 or value >= 0x110000: # never seen a unicode apple 2 before!
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return chr(value)
 @method(RValueLeftDollar)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value0 = self.children[0].get_str(context)
+  value1 = self.children[1].get_int(context)
+  if value1 < 0:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return value0[:value1]
 @method(RValueRightDollar)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value0 = self.children[0].get_str(context)
+  value1 = self.children[1].get_int(context)
+  if value1 < 0:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return '' if value1 == 0 else value0[-value1:]
 @method(RValueMidDollar)
 def get(self, context):
-  value = self.children[0].get(context)
-  assert False
+  value0 = self.children[0].get_str(context)
+  value1 = self.children[1].get_int(context)
+  if value1 < 1:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  value1 -= 1
+  if len(self.children) < 3:
+    return value0[value1:]
+  value2 = self.children[2].get_int(context)
+  if value2 < 0:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  return value0[value1:value1 + value2]
 @method(LValue)
 def get(self, context):
   value = self.find_variable(context).value
@@ -891,24 +940,29 @@ def get(self, context):
 del get
 
 @method(RValue)
-def get_float(self, context):
+def get_str(self, context):
   value = self.get(context)
-  if not isinstance(value, float):
+  if not isinstance(value, str):
     raise Exception(
       f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
     )
   return value
-del get_float
+del get_str
 
 @method(RValue)
-def get_str(self, context):
+def get_float(self, context):
   value = self.get(context)
-  if not isinstance(value, str):
+  if not isinstance(value, float):
     raise Exception(
       f'?TYPE MISMATCH ERROR IN {context.line_number():d}'
     )
   return value
-del get_str
+del get_float
+
+@method(RValue)
+def get_int(self, context):
+  return data_types.cint(self.get_float(context))
+del get_int
 
 @method(LValue)
 def find_variable(self, context):
index 8d53880..f4c2058 100644 (file)
@@ -324,7 +324,7 @@ rvalue
   | %space (?E{t_def.RValueAbs}TOKEN_ABS '(' rvalue ')')
   | %space (?E{t_def.RValueUsr}TOKEN_USR '(' rvalue ')')
   | %space (?E{t_def.RValueFre}TOKEN_FRE '(' rvalue ')')
-  | %space (?E{t_def.RValueScrnLParen}TOKEN_SCRN_LPAREN rvalue ')')
+  | %space (?E{t_def.RValueScrnLParen}TOKEN_SCRN_LPAREN rvalue ',' rvalue ')')
   | %space (?E{t_def.RValuePdl}TOKEN_PDL '(' rvalue ')')
   | %space (?E{t_def.RValuePos}TOKEN_POS '(' rvalue ')')
   | %space (?E{t_def.RValueSqr}TOKEN_SQR '(' rvalue ')')
@@ -341,9 +341,10 @@ rvalue
   | %space (?E{t_def.RValueVal}TOKEN_VAL '(' rvalue ')')
   | %space (?E{t_def.RValueAsc}TOKEN_ASC '(' rvalue ')')
   | %space (?E{t_def.RValueChrDollar}TOKEN_CHR_DOLLAR '(' rvalue ')')
-  | %space (?E{t_def.RValueLeftDollar}TOKEN_LEFT_DOLLAR '(' rvalue ')')
-  | %space (?E{t_def.RValueRightDollar}TOKEN_RIGHT_DOLLAR '(' rvalue ')')
-  | %space (?E{t_def.RValueMidDollar}TOKEN_MID_DOLLAR '(' rvalue ')')
+  | %space (?E{t_def.RValueLeftDollar}TOKEN_LEFT_DOLLAR '(' rvalue ',' rvalue ')')
+  | %space (?E{t_def.RValueRightDollar}TOKEN_RIGHT_DOLLAR '(' rvalue ',' rvalue ')')
+  | %space (?E{t_def.RValueMidDollar}TOKEN_MID_DOLLAR '(' rvalue ',' rvalue ')')
+  | %space (?E{t_def.RValueMidDollar}TOKEN_MID_DOLLAR '(' rvalue ',' rvalue ',' rvalue ')')
   ;
 
 lvalue
index e1e618c..5c81e65 100644 (file)
@@ -1,11 +1,14 @@
 import math
 
+# note: python 3 returns int type from math.floor(), I don't really
+# like this, but I rely on it below to avoid needing explicit int()
+
 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
+  return ((math.floor(value) + 0x8000) & 0xffff) - 0x8000
 
 def str_dollar(value):
   assert isinstance(value, float)
@@ -18,7 +21,7 @@ def str_dollar(value):
     value = '0.'
     exponent = ''
   else:
-    exponent = int(math.floor(math.log10(value)))
+    exponent = math.floor(math.log10(value))
     if exponent < -99:
       value = '0.'
       exponent = ''
index a35fa2c..fbfdfc9 100644 (file)
--- a/test.bas
+++ b/test.bas
 370 RESTORE
 380 READ M
 390 PRINT "M"M
-400 END
+400 M=3.14
+410 PRINT SGN(M)" "SGN(-M)
+420 PRINT ABS(M)" "ABS(-M)
+430 PRINT SQR(M)
+440 PRINT LOG(M)
+450 PRINT EXP(M)
+460 PRINT SIN(M)
+470 PRINT COS(M)
+480 PRINT TAN(M)
+490 PRINT ATN(M)
+500 PRINT ASC("A")
+510 PRINT CHR$(65)
+520 PRINT LEFT$("STRING",3)
+530 PRINT RIGHT$("STRING",3)
+540 PRINT MID$("STRING",3)
+550 PRINT MID$("STRING",3,2)
+560 PRINT INT(M)
+570 END
 1000 REM SUBROUTINE
 1010 I%=I%+1
 1020 RETURN