First cut at assembly passes, expressions, symbol table and binary output master
authorNick Downing <nick@ndcode.org>
Mon, 22 Jan 2024 12:54:39 +0000 (23:54 +1100)
committerNick Downing <nick@ndcode.org>
Mon, 22 Jan 2024 12:54:50 +0000 (23:54 +1100)
.gitignore
assemble.py [new file with mode: 0644]
evaluate.py [new file with mode: 0644]
operate.py [new file with mode: 0644]
vm_asm.py
vm_asm.t

index 3a1c769..1f455da 100644 (file)
@@ -1,3 +1,4 @@
+*.bin
 *.xml
 __pycache__
 /element.py
diff --git a/assemble.py b/assemble.py
new file mode 100644 (file)
index 0000000..40ae083
--- /dev/null
@@ -0,0 +1,114 @@
+import gmpy2
+import struct
+import t_def
+
+gmpy2.get_context().precision = 128
+
+sizes = {1, 2, 4, 8, 16}
+
+@t_def.method(t_def.Node)
+def assemble(self, context):
+  for i in self.children:
+    i.assemble(context)
+@t_def.method(t_def.Label)
+def assemble(self, context):
+  symbol = self.children[0].text[0]
+  if (
+    symbol not in context.symbols or
+    not isinstance(context.symbols[symbol], t_def.ValueInt) or
+    context.symbols[symbol].value != context.dot
+  ):
+    context.modified = True
+  context.symbols[symbol] = t_def.ValueInt(value = context.dot)
+@t_def.method(t_def.Equate)
+def assemble(self, context):
+  symbol = self.children[0].text[0]
+  context.symbols[symbol] = self.children[1].evaluate(context)
+@t_def.method(t_def.DotAlign)
+def assemble(self, context):
+  value = self.children[0].evaluate(context)
+  assert isinstance(value, t_def.ValueInt)
+  align = value.value
+  assert align in sizes
+  pad = -context.dot & (align - 1)
+  if context._pass:
+    context.fbin.write(bytes([0] * pad))
+  context.dot += pad
+@t_def.method(t_def.DotData)
+def assemble(self, context):
+  value = self.children[0].evaluate(context)
+  pad = -context.dot & (self.size - 1)
+  if context._pass:
+    context.fbin.write(bytes([0] * pad))
+  context.dot += pad
+  value = self.children[0].evaluate(context)
+  if self.type == t_def.TYPE_INT or self.type == t_def.TYPE_PCR:
+    if isinstance(value, t_def.ValueInt):
+      value = value.value
+    else:
+      assert False
+    if self.type == t_def.TYPE_PCR:
+      self.value -= context.dot - self.size
+    if context._pass:
+      context.fbin.write(
+        bytes([(value >> (i << 3)) & 0xff for i in range(self.size)])
+      )
+  elif self.type == t_def.TYPE_FLOAT:
+    if isinstance(value, t_def.ValueInt):
+      value = gmpy2.mpfr(value.value)
+    elif isinstance(value, t_def.ValueFloat):
+      value = value.value
+    else:
+      assert False
+    assert self.size == 4 # for now
+    if context._pass:
+      context.fbin.write(struct.pack('<f', float(value)))
+  else:
+    assert False
+  context.dot += self.size
+t_def.method(t_def.DotSpace)
+def assemble(self, context):
+  value = self.children[0].evaluate(context)
+  assert isinstance(value, t_def.ValueInt)
+  space = value.value
+  assert space >= 0
+  if context._pass:
+    context.fbin.write(bytes([0] * space))
+  context.dot += space
+@t_def.method(t_def.Op)
+def assemble(self, context):
+  if context._pass:
+    context.fbin.write(bytes([self.op]))
+  context.dot += 1
+@t_def.method(t_def.OpData)
+def assemble(self, context):
+  t_def.Op.assemble(self, context)
+  pad = -context.dot & (self.size - 1)
+  if context._pass:
+    context.fbin.write(bytes([0] * pad))
+  context.dot += pad
+  value = self.children[0].evaluate(context)
+  if self.type == t_def.TYPE_INT or self.type == t_def.TYPE_PCR:
+    if isinstance(value, t_def.ValueInt):
+      value = value.value
+    else:
+      assert False
+    if self.type == t_def.TYPE_PCR:
+      value -= context.dot - self.size
+    if context._pass:
+      context.fbin.write(
+        bytes([(value >> (i << 3)) & 0xff for i in range(self.size)])
+      )
+  elif self.type == t_def.TYPE_FLOAT:
+    if isinstance(value, t_def.ValueInt):
+      value = gmpy2.mpfr(value.value)
+    elif isinstance(value, t_def.ValueFloat):
+      value = value.value
+    else:
+      assert False
+    assert self.size == 4 # for now
+    if context._pass:
+      context.fbin.write(struct.pack('<f', float(value)))
+  else:
+    assert False
+  context.dot += self.size
diff --git a/evaluate.py b/evaluate.py
new file mode 100644 (file)
index 0000000..f1dc7a5
--- /dev/null
@@ -0,0 +1,118 @@
+import gmpy2
+import t_def
+
+gmpy2.get_context().precision = 128
+mpfr_0 = gmpy2.mpfr('0')
+
+def to_bool(value):
+  if isinstance(value, t_def.ValueInt):
+    return value.value != 0
+  if isinstance(value, t_def.ValueFloat):
+    return value.value != mpfr_0
+  assert False
+
+@t_def.method(t_def.Expression)
+def evaluate(self, context):
+  print(self)
+  raise NotImplementedError()
+@t_def.method(t_def.ExpressionUnary)
+def evaluate(self, context):
+  value = self.children[0].evaluate(context)
+  if isinstance(value, t_def.ValueInt):
+    return self.operate_int(value.value)
+  if isinstance(value, t_def.ValueFloat):
+    return self.operate_float(value.value)
+  assert False
+@t_def.method(t_def.ExpressionLogicalNot)
+def evaluate(self, context):
+  return t_def.ValueInt(
+    value = int(not to_bool(self.children[0].evaluate(context)))
+  )
+@t_def.method(t_def.ExpressionBinary)
+def evaluate(self, context):
+  value0 = self.children[0].evaluate(context)
+  value1 = self.children[1].evaluate(context)
+  if isinstance(value0, t_def.ValueInt):
+    if isinstance(value1, t_def.ValueInt):
+      return self.operate_int(value0.value, value1.value)
+    if isinstance(value1, t_def.ValueFloat):
+      return self.operate_float(gmpy2.mpfr(value0.value), value1.value)
+    assert False
+  if isinstance(value0, t_def.ValueFloat):
+    if isinstance(value1, t_def.ValueInt):
+      return self.operate_float(value0.value, gmpy2.mpfr(value1.value))
+    if isinstance(value1, t_def.ValueFloat):
+      return self.operate_float(value0.value, value1.value)
+    assert False
+  assert False
+@t_def.method(t_def.ExpressionLogicalOr)
+def evaluate(self, context):
+  return t_def.ValueInt(
+    value = int(
+      to_bool(self.children[0].evaluate(context)) or 
+      to_bool(self.children[1].evaluate(context))
+    )
+  )
+@t_def.method(t_def.ExpressionLogicalAnd)
+def evaluate(self, context):
+  return t_def.ValueInt(
+    value = int(
+      to_bool(self.children[0].evaluate(context)) and
+      to_bool(self.children[1].evaluate(context))
+    )
+  )
+@t_def.method(t_def.ExpressionCharacterConstant)
+def evaluate(self, context):
+  value = 0
+  for i in self.children:
+    if isinstance(i, t_def.Text):
+      for j in i.text[0]:
+        value = (value << 8) | (ord(j) & 0xff)
+    elif isinstance(i, t_def.SimpleEscapeSequence):
+      value = (value << 8) | (i.value & 0xff)
+    elif isinstance(i, t_def.IntegerEscapeSequence):
+      value = (value << 8) | (int(i.children[0].text[0], i.base) & 0xff)
+    else:
+      assert False
+  return t_def.ValueInt(value = value)
+@t_def.method(t_def.ExpressionConditional)
+def evaluate(self, context):
+  return self.children[
+    1 if to_bool(self.children[0].evaluate(context)) else 2
+  ].evaluate(context)
+@t_def.method(t_def.ExpressionFloatingConstant)
+def evaluate(self, context):
+  digits0 = self.children[0].text[0]
+  digits1 = self.children[1].text[0]
+  exponent_part = self.children[2]
+  exponent = (
+    0
+  if len(exponent_part.children) == 0 else
+    exponent_part.children[0].value * int(exponent_part.children[1].text[0])
+  )
+  return t_def.ValueFloat(
+    value = int(digits0 + digits1) * gmpy2.exp10(exponent - len(digits1))
+  )
+@t_def.method(t_def.ExpressionIdentifier)
+def evaluate(self, context):
+  if context._pass == 0:
+    return t_def.ValueInt(value = context.dot)
+  symbol = self.text[0]
+  return context.symbols[symbol]
+@t_def.method(t_def.ExpressionIntegerConstant)
+def evaluate(self, context):
+  return t_def.ValueInt(value = int(self.children[0].text[0], self.base))
+@t_def.method(t_def.ExpressionStringLiteral)
+def evaluate(self, context):
+  values = []
+  for i in self.children:
+    if isinstance(i, t_def.Text):
+      values.extend([ord(j) & 0xff for j in i.text[0]])
+    elif isinstance(i, t_def.SimpleEscapeSequence):
+      values.append(i.value & 0xff)
+    elif isinstance(i, t_def.IntegerEscapeSequence):
+      values.append(int(i.children[0].text[0], i.base) & 0xff)
+    else:
+      assert False
+  return t_def.ValueString(values = values)
+del evaluate
diff --git a/operate.py b/operate.py
new file mode 100644 (file)
index 0000000..f6dabd3
--- /dev/null
@@ -0,0 +1,122 @@
+import gmpy2
+import t_def
+
+@t_def.method(t_def.ExpressionUnary)
+def operate_int(self, value):
+  print(self)
+  raise NotImplementedError()
+@t_def.method(t_def.ExpressionBitwiseNot)
+def operate_int(self, value):
+  return t_def.ValueInt(value = ~value)
+@t_def.method(t_def.ExpressionMinus)
+def operate_int(self, value):
+  return t_def.ValueInt(value = -value)
+@t_def.method(t_def.ExpressionPlus)
+def operate_int(self, value):
+  return t_def.ValueInt(value = value)
+del operate_int
+
+@t_def.method(t_def.ExpressionUnary)
+def operate_float(self, value):
+  print(self)
+  raise NotImplementedError()
+@t_def.method(t_def.ExpressionMinus)
+def operate_float(self, value):
+  return t_def.ValueInt(value = -value)
+@t_def.method(t_def.ExpressionPlus)
+def operate_float(self, value):
+  return t_def.ValueInt(value = value)
+del operate_float
+
+@t_def.method(t_def.ExpressionBinary)
+def operate_int(self, value0, value1):
+  print(self)
+  raise NotImplementedError()
+@t_def.method(t_def.ExpressionAdd)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 + value1)
+@t_def.method(t_def.ExpressionBitwiseAnd)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 & value1)
+@t_def.method(t_def.ExpressionBitwiseOr)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 | value1)
+@t_def.method(t_def.ExpressionDivide)
+def operate_int(self, value0, value1):
+  sign0 = -1 if value0 < 0 else 1
+  sign1 = -1 if value1 < 0 else 1
+  return t_def.ValueInt(value = sign0 * sign1 * (abs(value0) // abs(value1)))
+@t_def.method(t_def.ExpressionExclusiveOr)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 ^ value1)
+@t_def.method(t_def.ExpressionModulo)
+def operate_int(self, value0, value1):
+  sign0 = -1 if value0 < 0 else 1
+  return t_def.ValueInt(value = sign0 * (abs(value0) % abs(value1)))
+@t_def.method(t_def.ExpressionMultiply)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 * value1)
+@t_def.method(t_def.ExpressionShiftLeft)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 << value1 & 0xff)
+@t_def.method(t_def.ExpressionShiftRight)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 >> value1 & 0xff)
+@t_def.method(t_def.ExpressionSubtract)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = value0 - value1)
+@t_def.method(t_def.ExpressionEqual)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 == value1))
+@t_def.method(t_def.ExpressionGreaterThan)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 > value1))
+@t_def.method(t_def.ExpressionGreaterThanOrEqual)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 >= value1))
+@t_def.method(t_def.ExpressionLessThan)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 < value1))
+@t_def.method(t_def.ExpressionLessThanOrEqual)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 <= value1))
+@t_def.method(t_def.ExpressionNotEqual)
+def operate_int(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 != value1))
+del operate_int
+
+@t_def.method(t_def.ExpressionBinary)
+def operate_float(self, value0, value1):
+  print(self)
+  raise NotImplementedError()
+@t_def.method(t_def.ExpressionAdd)
+def operate_float(self, value0, value1):
+  return t_def.ValueFloat(value = value0 + value1)
+@t_def.method(t_def.ExpressionDivide)
+def operate_float(self, value0, value1):
+  return t_def.ValueFloat(value = value0 / value1)
+@t_def.method(t_def.ExpressionMultiply)
+def operate_float(self, value0, value1):
+  return t_def.ValueFloat(value = value0 * value1)
+@t_def.method(t_def.ExpressionSubtract)
+def operate_float(self, value0, value1):
+  return t_def.ValueFloat(value = value0 - value1)
+@t_def.method(t_def.ExpressionEqual)
+def operate_float(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 == value1))
+@t_def.method(t_def.ExpressionGreaterThan)
+def operate_float(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 > value1))
+@t_def.method(t_def.ExpressionGreaterThanOrEqual)
+def operate_float(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 >= value1))
+@t_def.method(t_def.ExpressionLessThan)
+def operate_float(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 < value1))
+@t_def.method(t_def.ExpressionLessThanOrEqual)
+def operate_float(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 <= value1))
+@t_def.method(t_def.ExpressionNotEqual)
+def operate_float(self, value0, value1):
+  return t_def.ValueInt(value = int(value0 != value1))
+del operate_float
index 2c0bdfd..c33d2d2 100755 (executable)
--- a/vm_asm.py
+++ b/vm_asm.py
@@ -22,6 +22,11 @@ import sys
 import t_def
 import y_tab
 
+# by importing these we make sure they run, to add methods onto t_def classes
+import assemble
+import evaluate
+import operate
+
 EXIT_SUCCESS = 0
 EXIT_FAILURE = 1
 
@@ -29,16 +34,36 @@ EXIT_FAILURE = 1
 #print('yytext', f'"{lex_yy.yytext:s}"')
 #assert False
 
-if len(sys.argv) < 2:
-  print(f'usage: {sys.argv[0]:s} in_file.asm')
+if len(sys.argv) < 3:
+  print(f'usage: {sys.argv[0]:s} in_file.asm out_file.bin')
   sys.exit(EXIT_FAILURE)
 in_file_asm = sys.argv[1]
+out_file_bin = sys.argv[2]
 
 with open(in_file_asm) as fin:
   lex_yy.yyin = fin
   y_tab.in_file = in_file_asm
-  expression = y_tab.yyparse(t_def.InputFile)
-expression.post_process(t_def.Context())
-element.serialize(expression, 'a.xml', 'utf-8')
-expression = element.deserialize('a.xml', t_def.factory, 'utf-8')
-element.serialize(expression, 'b.xml', 'utf-8')
+  input_file = y_tab.yyparse(t_def.InputFile)
+
+context = t_def.Context()
+while True:
+  print('pass', context._pass)
+
+  prev_dot = context.dot
+  context.dot = 0
+  context.modified = False
+
+  if context._pass:
+    context.fbin = open(out_file_bin, 'wb')
+  input_file.assemble(context)
+  if context._pass:
+    context.fbin.close()
+
+  if not context.modified and context.dot == prev_dot:
+    break
+
+  context._pass += 1
+
+element.serialize(input_file, 'a.xml', 'utf-8')
+input_file = element.deserialize('a.xml', t_def.factory, 'utf-8')
+element.serialize(input_file, 'b.xml', 'utf-8')
index 683a646..3de6eac 100644 (file)
--- a/vm_asm.t
+++ b/vm_asm.t
@@ -479,7 +479,7 @@ class ValueExtern: Value {
 /*
  * all of the following classes can appear as children or indirect children
  * of the InputFile (root) node, and have Node as a superclass so that
- * the post_process() routine can effortlessly recurse through all children
+ * the assemble() routine can effortlessly recurse through all children
  */
 
 /* internal */
@@ -534,6 +534,9 @@ class ExpressionNotEqual: ExpressionBinary;
 class ExpressionPlus: ExpressionUnary;
 class ExpressionShiftLeft: ExpressionBinary;
 class ExpressionShiftRight: ExpressionBinary;
+class ExpressionStringLiteral: Expression {
+  bool wide;
+};
 class ExpressionSubtract: ExpressionBinary;
 class Identifier: Node;
 class Label: InputLine;
@@ -559,8 +562,16 @@ def factory(tag, *args, **kwargs):
   return tag_to_class[tag](*args, **kwargs)
 
 class Context:
-  pass
-
-@method(Node)
-def post_process(self, context):
-  pass
+  def __init__(
+    self,
+    _pass = 0,
+    modified = False,
+    dot = 0,
+    symbols = None,
+    fbin = None
+  ):
+    self._pass = _pass
+    self.modified = modified
+    self.dot = dot
+    self.symbols = {} if symbols is None else symbols
+    self.fbin = fbin