Improve VAL() backend and handling of invalid numbers in INPUT/READ statements
authorNick Downing <nick@ndcode.org>
Sun, 22 May 2022 11:40:32 +0000 (21:40 +1000)
committerNick Downing <nick@ndcode.org>
Sun, 22 May 2022 11:43:48 +0000 (21:43 +1000)
applesoft_basic.t
data_types.py

index 546a564..aa3dcc8 100644 (file)
@@ -383,13 +383,25 @@ def execute(self, context):
   pass
 @method(StatementInput)
 def execute(self, context):
-  apple_io._print(
-    self.children[-2].text[0]
-  if len(self.children) >= 2 else
-    '?'
-  )
-  value = apple_io.input()
-  self.children[-1].set_val(context, value)
+  while True:
+    apple_io._print(
+      self.children[-2].text[0]
+    if len(self.children) >= 2 else
+      '?'
+    )
+    value = apple_io.input()
+    lvalue = self.children[-1]
+    name = lvalue.children[0].str_value
+    if name[-1] != '$':
+      n, result = data_types.val(value)
+      if n == 0 or n < len(value):
+        apple_io._print('?REENTER\r')
+        continue
+      value = result
+      if name[-1] == '%':
+        value = data_types.cint(value)
+    lvalue.find_variable(context).value = value
+    break
 @method(StatementDel)
 def execute(self, context):
   assert False
@@ -425,8 +437,20 @@ def execute(self, context):
         while context.m < len(statement.children):
           item = statement.children[context.m]
           context.m += 1
-          self.children[i].set_val(context, item.text[0])
+          value = item.text[0]
+          lvalue = self.children[i]
           i += 1
+          name = lvalue.children[0].str_value
+          if name[-1] != '$':
+            n, result = data_types.val(value)
+            if n < len(value):
+              raise Exception(
+                f'?SYNTAX ERROR IN {context.line_number():d}'
+              )
+            value = result
+            if name[-1] == '%':
+              value = data_types.cint(value)
+          lvalue.find_variable(context).value = value
           if i >= len(self.children):
             return
       context.l += 1
@@ -659,7 +683,7 @@ def execute(self, context):
   for i in self.children:
     value = i.get(context)
     if isinstance(value, float):
-      value = data_types.str_dollar(value)
+      value = data_types.str_dollar(context, value)
     apple_io._print(value)
   if not self.semicolon:
     apple_io._print('\r')
@@ -900,11 +924,11 @@ def get(self, context):
 @method(RValueStrDollar)
 def get(self, context):
   value = self.children[0].get_float(context)
-  return data_types.str_dollar(value)
+  return data_types.str_dollar(context, value)
 @method(RValueVal)
 def get(self, context):
   value = self.children[0].get_str(context)
-  return data_types.val(value)
+  return data_types.val(value)[1] # ignore n chars converted
 @method(RValueAsc)
 def get(self, context):
   value = self.children[0].get_str(context)
@@ -1038,14 +1062,3 @@ def set(self, context, value):
       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 5c81e65..d2d816a 100644 (file)
@@ -1,4 +1,5 @@
 import math
+import re
 
 # 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()
@@ -10,7 +11,7 @@ def cint(value):
   assert isinstance(value, float)
   return ((math.floor(value) + 0x8000) & 0xffff) - 0x8000
 
-def str_dollar(value):
+def str_dollar(context, value):
   assert isinstance(value, float)
   sign = ''
   if value < 0.:
@@ -67,8 +68,45 @@ def str_dollar(value):
     i += 1
   return sign + value[:i] + exponent
 
+# (\ *[+-])?           1
+# (                    2
+#   (                  3
+#     (\ *[0-9]*)*     4
+#   )
+#   \ *\.
+#   (                  5
+#     (\ *[0-9])*      6
+#   )
+#   |
+#   (                  7
+#     (\ *[0-9])+      8
+#   )
+# )
+# (                    9
+#   \ *E
+#   (\ *[+-])?         10
+#   \ *
+#   (                  11
+#     (\ *[0-9])+      12
+#   )
+# )?
+re_val = re.compile(
+  '(\ *[+-])?(((\ *[0-9]*)*)\ *\.((\ *[0-9])*)|((\ *[0-9])+))(\ *E(\ *[+-])?\ *((\ *[0-9])+))?'
+)
 def val(value):
-  # make this more sophisticated later
-  assert isinstance(value, str)
-  value = value.replace(' ', '')
-  return float(value) if len(value) else 0.
+  match = re_val.match(value)
+  if match is None:
+    return 0, 0.
+  sign = (match.group(1) or '').replace(' ', '')
+  integer = (match.group(3) or match.group(7) or '').replace(' ', '')
+  fraction = (match.group(5) or '').replace(' ', '')
+  exp = 0
+  if match.group(9) is not None:
+    exp_sign = (match.group(10) or '').replace(' ', '')
+    exp = int(match.group(11).replace(' ', ''))
+    if exp_sign == '-':
+      exp = -exp
+  result = int(integer + fraction) * 10. ** (exp - len(fraction))
+  if sign == '-':
+    result = -result
+  return len(match.group(0)), result