Use tests_ast/cal_py.t, get rid of old AST generator and stripper
authorNick Downing <nick@ndcode.org>
Mon, 28 Jan 2019 03:13:14 +0000 (14:13 +1100)
committerNick Downing <nick@ndcode.org>
Mon, 28 Jan 2019 10:35:47 +0000 (21:35 +1100)
.gitignore
degenerate.py [deleted file]
generate_ast.py [deleted file]
tests_ast/Makefile
tests_ast/ast.py [deleted file]
tests_ast/ast.sh [deleted file]
tests_ast/cal.py
tests_ast/cal_py.l
tests_ast/cal_py.t [new file with mode: 0644]
tests_ast/cal_py.y
tests_ast/element.py

index a91d2cb..22c0f39 100644 (file)
@@ -1,5 +1,4 @@
 __pycache__
-/*.xml
 /bootstrap/*.xml
 /bootstrap/lex_yy.py
 /bootstrap/lex_yy_code.py
@@ -24,7 +23,7 @@ __pycache__
 /tests/y_tab.py
 /tests/cal
 /tests/cal2
-/tests_ast/*.xml
 /tests_ast/lex_yy.py
+/tests_ast/t_def.py
 /tests_ast/y_tab.py
 /y_tab.py
diff --git a/degenerate.py b/degenerate.py
deleted file mode 100755 (executable)
index 4a68de0..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2018 Nick Downing <nick@ndcode.org>
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-import re
-import sys
-
-re_begin = re.compile(
-  '([\t ]*# GENERATE .*) BEGIN'
-)
-re_end = re.compile(
-  '([\t ]*# GENERATE END)'
-)
-
-line = sys.stdin.readline()
-while len(line):
-  match = re_begin.match(line)
-  if match is not None:
-    sys.stdout.write(match.group(1))
-    line = sys.stdin.readline()
-    while len(line):
-      match = re_end.match(line)
-      if match is not None:
-        sys.stdout.write(line[len(match.group(1)):])
-        break
-      line = sys.stdin.readline()
-  else:
-    sys.stdout.write(line)
-  line = sys.stdin.readline()
diff --git a/generate_ast.py b/generate_ast.py
deleted file mode 100755 (executable)
index 8904655..0000000
+++ /dev/null
@@ -1,412 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2019 Nick Downing <nick@ndcode.org>
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-import re
-import sys
-
-if len(sys.argv) >= 2:
-  package_name = '{0:s}.'.format(sys.argv[1])
-else:
-  package_name = ''
-
-default_value = {
-  'bool': 'False',
-  'int': '-1',
-  'ref': 'None',
-  'str': '\'\'',
-  'list(bool)': '[]',
-  'list(int)': '[]',
-  'list(ref)': '[]',
-  'list(str)': '[]',
-  'set(bool)': 'set()',
-  'set(int)': 'set()',
-  'set(ref)': 'set()',
-  'set(str)': 'set()'
-}
-default_value_str = {
-  'bool': 'false',
-  'int': '-1',
-  'ref': '-1',
-  'str': ''
-}
-
-re_class = re.compile(
-  '([\t ]*)class ([A-Za-z_][A-Za-z0-9_]*)\(([A-Za-z_][A-Za-z0-9_.]*)'
-)
-re_element = re.compile(
-  '([\t ]*)# GENERATE ELEMENT\((([^()]|\([^()]*\))*)\)( BEGIN)?'
-)
-re_factory = re.compile(
-  '([\t ]*)# GENERATE FACTORY\(([^()]*)\)( BEGIN)?'
-)
-stack = []
-classes = []
-base_classes = [{'element.Element': []}] # params
-
-line = sys.stdin.readline()
-while len(line):
-  match = re_class.match(line)
-  if match is not None:
-    sys.stdout.write(line)
-    indent = match.group(1)
-    class_name = match.group(2)
-    base_class = match.group(3)
-    while len(stack) and stack[-1][0][:len(indent)] == indent:
-      _, temp_class_name, _, _ = stack.pop()
-      for temp_base_class, temp_fields in base_classes.pop().items():
-        base_classes[-1][
-          '{0:s}.{1:s}'.format(temp_class_name, temp_base_class)
-        ] = temp_fields
-    for i in range(len(base_classes) - 1, -1, -1):
-      if base_class in base_classes[i]:
-        classes.append(
-          '.'.join([j for _, j, _, _ in stack] + [class_name])
-        )
-        full_base_class = '.'.join(
-          [j for _, j, _, _ in stack[:i]] + [base_class]
-        )
-        base_classes[-1][class_name] = list(base_classes[i][base_class])
-        break
-    else:
-      full_base_class = base_class
-    stack.append((indent, class_name, base_class, full_base_class))
-    base_classes.append({})
-  else:
-    match = re_element.match(line)
-    if match is not None:
-      indent = match.group(1)
-      params = match.group(2)
-      begin = match.group(4)
-
-      while len(stack) and stack[-1][0][:len(indent)] == indent:
-        _, temp_class_name, _, _ = stack.pop()
-        for temp_base_class, temp_fields in base_classes.pop().items():
-          base_classes[-1][
-            '{0:s}.{1:s}'.format(temp_class_name, temp_base_class)
-          ] = temp_fields
-      _, class_name, base_class, full_base_class = stack[-1]
-      fields = params.split(',')
-      if fields[-1] == '':
-        del fields[-1:]
-      fields = [i.split() for i in fields]
-      fields = [(type, name) for [type, name] in fields]
-      i = len(base_classes[-2][class_name])
-      base_classes[-2][class_name].extend(fields)
-
-      sys.stdout.write(
-        '''{0:s}# GENERATE ELEMENT({1:s}) BEGIN
-{2:s}def __init__(
-{3:s}  self,
-{4:s}  tag = '{5:s}',
-{6:s}  attrib = {{}},
-{7:s}  text = '',
-{8:s}  children = []{9:s}
-{10:s}):
-{11:s}  {12:s}.__init__(
-{13:s}    self,
-{14:s}    tag,
-{15:s}    attrib,
-{16:s}    text,
-{17:s}    children{18:s}
-{19:s}  )
-{20:s}'''.format(
-          indent,
-          params,
-          indent,
-          indent,
-          indent,
-          '_'.join([i for _, i, _, _ in stack]),
-          indent,
-          indent,
-          indent,
-          ''.join(
-            [
-              ',\n{0:s}  {1:s} = {2:s}'.format(
-                indent,
-                name,
-                default_value[type]
-              )
-              for type, name in base_classes[-2][class_name]
-            ]
-          ),
-          indent,
-          indent,
-          full_base_class,
-          indent,
-          indent,
-          indent,
-          indent,
-          indent,
-          ''.join(
-            [
-              ',\n{0:s}    {1:s}'.format(
-                indent,
-                name
-              )
-              for _, name in base_classes[-2][class_name][:i]
-            ]
-          ),
-          indent,
-          ''.join(
-            [
-              '{0:s}  self.{1:s} = {2:s}\n'.format(
-                indent,
-                name,
-                name
-              )
-              for _, name in fields
-            ]
-          )
-        )
-      )
-      if len(fields):
-        sys.stdout.write(
-          '''{0:s}def serialize(self, ref_list):
-{1:s}  {2:s}.serialize(self, ref_list)
-'''.format(
-            indent,
-            indent,
-            full_base_class
-          )
-        )
-        for type, name in fields:
-          if type[:5] == 'list(' and type[-1:] == ')':
-            subtype = type[5:-1]
-            sys.stdout.write(
-              '''{0:s}  self.set(
-{1:s}    '{2:s}',
-{3:s}    ' '.join(
-{4:s}      [
-{5:s}        element.serialize_{6:s}(i{7:s})
-{8:s}        for i in self.{9:s}
-{10:s}      ]
-{11:s}    )
-{12:s}  )
-'''.format(
-                indent,
-                indent,
-                name,
-                indent,
-                indent,
-                indent,
-                subtype,
-                ', ref_list' if subtype == 'ref' else '',
-                indent,
-                name,
-                indent,
-                indent,
-                indent
-              )
-            )
-          elif type[:4] == 'set(' and type[-1:] == ')':
-            subtype = type[4:-1]
-            sys.stdout.write(
-              '''{0:s}  self.set(
-{1:s}    '{2:s}',
-{3:s}    ' '.join(
-{4:s}      [
-{5:s}        element.serialize_{6:s}(i{7:s})
-{8:s}        for i in sorted(self.{9:s})
-{10:s}      ]
-{11:s}    )
-{12:s}  )
-'''.format(
-                indent,
-                indent,
-                name,
-                indent,
-                indent,
-                indent,
-                subtype,
-                ', ref_list' if subtype == 'ref' else '',
-                indent,
-                name,
-                indent,
-                indent,
-                indent
-              )
-            )
-          else:
-            sys.stdout.write(
-              '''{0:s}  self.set(
-{1:s}    '{2:s}',
-{3:s}    element.serialize_{4:s}(self.{5:s}{6:s})
-{7:s}  )
-'''.format(
-                indent,
-                indent,
-                name,
-                indent,
-                type,
-                name,
-                ', ref_list' if type == 'ref' else '',
-                indent
-              )
-            )
-        sys.stdout.write(
-          '''{0:s}def deserialize(self, ref_list):
-{1:s}  {2:s}.deserialize(self, ref_list)
-'''.format(
-            indent,
-            indent,
-            full_base_class
-          )
-        )
-        for type, name in fields:
-          if type[:5] == 'list(' and type[-1:] == ')':
-            subtype = type[5:-1]
-            sys.stdout.write(
-              '''{0:s}  self.{1:s} = [
-{2:s}    element.deserialize_{3:s}(i{4:s})
-{5:s}    for i in self.get('{6:s}', '').split()
-{7:s}  ]
-'''.format(
-                indent,
-                name,
-                indent,
-                subtype,
-                ', ref_list' if subtype == 'ref' else '',
-                indent,
-                name,
-                indent
-              )
-            )
-          elif type[:4] == 'set(' and type[-1:] == ')':
-            subtype = type[4:-1]
-            sys.stdout.write(
-              '''{0:s}  self.{1:s} = set(
-{2:s}    [
-{3:s}      element.deserialize_{4:s}(i{5:s})
-{6:s}      for i in self.get('{7:s}', '').split()
-{8:s}    ]
-{9:s}  )
-'''.format(
-                indent,
-                name,
-                indent,
-                indent,
-                subtype,
-                ', ref_list' if subtype == 'ref' else '',
-                indent,
-                name,
-                indent,
-                indent
-              )
-            )
-          else: 
-            sys.stdout.write(
-              '''{0:s}  self.{1:s} = element.deserialize_{2:s}(self.get('{3:s}', '{4:s}'){5:s})
-'''.format(
-                indent,
-                name,
-                type,
-                name,
-                default_value_str[type],
-                ', ref_list' if type == 'ref' else ''
-              )
-            )
-      sys.stdout.write(
-        '''{0:s}def copy(self, factory = None):
-{1:s}  result = {2:s}.copy(
-{3:s}    self,
-{4:s}    {5:s} if factory is None else factory
-{6:s}  ){7:s}
-{8:s}  return result
-'''.format(
-          indent,
-          indent,
-          full_base_class,
-          indent,
-          indent,
-          class_name,
-          indent,
-          ''.join(
-            [
-              '\n{0:s}  result.{1:s} = self.{2:s}'.format(
-                indent,
-                name,
-                name
-              )
-              for _, name in fields
-            ]
-          ),
-          indent
-        )
-      )
-      sys.stdout.write(
-        '''{0:s}# GENERATE END
-'''.format(
-          indent
-        )
-      )
-      if begin is not None:
-        line = sys.stdin.readline()
-        while len(line):
-          if line.strip() == '# GENERATE END':
-            break
-          line = sys.stdin.readline()
-        else:
-          assert False
-    else:
-      match = re_factory.match(line)
-      if match is not None:
-        indent = match.group(1)
-        param = match.group(2)
-        begin = match.group(3)
-
-        sys.stdout.write(
-          '''{0:s}# GENERATE FACTORY({1:s}) BEGIN
-{2:s}tag_to_class = {{{3:s}
-{4:s}}}
-{5:s}def factory(tag, attrib = {{}}, *args, **kwargs):
-{6:s}  return tag_to_class.get(tag, {7:s})(tag, attrib, *args, **kwargs)
-{8:s}# GENERATE END
-'''.format(
-            indent,
-            param,
-            indent,
-            ','.join(
-              [
-                '\n{0:s}  \'{1:s}\': {2:s}'.format(
-                  indent,
-                  i.replace('.', '_'),
-                  i
-                )
-                for i in classes
-              ]
-            ),
-            indent,
-            indent,
-            indent,
-            param,
-            indent
-          )
-        )
-
-        if begin is not None:
-          line = sys.stdin.readline()
-          while len(line):
-            if line.strip() == '# GENERATE END':
-              break
-            line = sys.stdin.readline()
-          else:
-            assert False
-      else:
-        sys.stdout.write(line)
-  line = sys.stdin.readline()
index f245b74..a342330 100644 (file)
@@ -1,12 +1,13 @@
-all: lex_yy.py y_tab.py
+all: lex_yy.py t_def.py y_tab.py
 
 lex_yy.py: cal_py.l
-       ../../bootstrap_flex.git/src/flex -o /dev/null $< 2>$<.xml
-       ../../pilex.git/pilex.py --element --python $<.xml
+       ../../pilex.git/pilex.py --element --python $<
+
+t_def.py: cal_py.t
+       ../../pitree.git/pitree.py --python $<
 
 y_tab.py: cal_py.y
-       ../../bootstrap_bison.git/src/bison -o /dev/null $< 2>$<.xml
-       ../piyacc.py --element --python $<.xml
+       ../piyacc.py --element --python $<
 
 clean:
-       rm -f lex_yy.py y_tab.py *.xml
+       rm -f lex_yy.py t_def.py y_tab.py
diff --git a/tests_ast/ast.py b/tests_ast/ast.py
deleted file mode 100644 (file)
index 6293472..0000000
+++ /dev/null
@@ -1,317 +0,0 @@
-# Copyright (C) 2018 Nick Downing <nick@ndcode.org>
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-import element
-
-class Text(element.Element):
-  # GENERATE ELEMENT() BEGIN
-  def __init__(
-    self,
-    tag = 'Text',
-    attrib = {},
-    text = '',
-    children = []
-  ):
-    element.Element.__init__(
-      self,
-      tag,
-      attrib,
-      text,
-      children
-    )
-  def copy(self, factory = None):
-    result = element.Element.copy(
-      self,
-      Text if factory is None else factory
-    )
-    return result
-  # GENERATE END
-  def get_text(self):
-    return element.get_text(self, 0)
-
-class AST(element.Element):
-  class Expr(element.Element):
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Expr',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      element.Element.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = element.Element.copy(
-        self,
-        Expr if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      raise NotImplementedException()
-
-  class Num(Expr):
-    class Mantissa(Text):
-      # GENERATE ELEMENT() BEGIN
-      def __init__(
-        self,
-        tag = 'AST_Num_Mantissa',
-        attrib = {},
-        text = '',
-        children = []
-      ):
-        Text.__init__(
-          self,
-          tag,
-          attrib,
-          text,
-          children
-        )
-      def copy(self, factory = None):
-        result = Text.copy(
-          self,
-          Mantissa if factory is None else factory
-        )
-        return result
-      # GENERATE END
-
-    class Fraction(Text):
-      # GENERATE ELEMENT() BEGIN
-      def __init__(
-        self,
-        tag = 'AST_Num_Fraction',
-        attrib = {},
-        text = '',
-        children = []
-      ):
-        Text.__init__(
-          self,
-          tag,
-          attrib,
-          text,
-          children
-        )
-      def copy(self, factory = None):
-        result = Text.copy(
-          self,
-          Fraction if factory is None else factory
-        )
-        return result
-      # GENERATE END
-
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Num',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      AST.Expr.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = AST.Expr.copy(
-        self,
-        Num if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      mantissa = self[0].get_text()
-      fraction = self[1].get_text() if len(self) >= 2 else ''
-      return int(mantissa + fraction) * 10 ** -len(fraction)
-
-  class Add(element.Element):
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Add',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      element.Element.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = element.Element.copy(
-        self,
-        Add if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      return self[0].eval() + self[1].eval()
-
-  class Sub(element.Element):
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Sub',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      element.Element.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = element.Element.copy(
-        self,
-        Sub if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      return self[0].eval() - self[1].eval()
-
-  class Mul(element.Element):
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Mul',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      element.Element.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = element.Element.copy(
-        self,
-        Mul if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      return self[0].eval() * self[1].eval()
-
-  class Div(element.Element):
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Div',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      element.Element.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = element.Element.copy(
-        self,
-        Div if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      return self[0].eval() / self[1].eval()
-
-  class Neg(element.Element):
-    # GENERATE ELEMENT() BEGIN
-    def __init__(
-      self,
-      tag = 'AST_Neg',
-      attrib = {},
-      text = '',
-      children = []
-    ):
-      element.Element.__init__(
-        self,
-        tag,
-        attrib,
-        text,
-        children
-      )
-    def copy(self, factory = None):
-      result = element.Element.copy(
-        self,
-        Neg if factory is None else factory
-      )
-      return result
-    # GENERATE END
-    def eval(self):
-      return -self[0].eval()
-
-  # GENERATE ELEMENT() BEGIN
-  def __init__(
-    self,
-    tag = 'AST',
-    attrib = {},
-    text = '',
-    children = []
-  ):
-    element.Element.__init__(
-      self,
-      tag,
-      attrib,
-      text,
-      children
-    )
-  def copy(self, factory = None):
-    result = element.Element.copy(
-      self,
-      AST if factory is None else factory
-    )
-    return result
-  # GENERATE END
-
-# GENERATE FACTORY(element.Element) BEGIN
-tag_to_class = {
-  'Text': Text,
-  'AST': AST,
-  'AST_Expr': AST.Expr,
-  'AST_Num': AST.Num,
-  'AST_Num_Mantissa': AST.Num.Mantissa,
-  'AST_Num_Fraction': AST.Num.Fraction,
-  'AST_Add': AST.Add,
-  'AST_Sub': AST.Sub,
-  'AST_Mul': AST.Mul,
-  'AST_Div': AST.Div,
-  'AST_Neg': AST.Neg
-}
-def factory(tag, attrib = {}, *args, **kwargs):
-  return tag_to_class.get(tag, element.Element)(tag, attrib, *args, **kwargs)
-# GENERATE END
diff --git a/tests_ast/ast.sh b/tests_ast/ast.sh
deleted file mode 100755 (executable)
index 45b2e1c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-if ../generate_ast.py ast <ast.py >ast.py.new && ! diff -q ast.py ast.py.new
-then
-  mv ast.py.new ast.py
-else
-  rm -f ast.py.new
-fi
index ccd1cde..94870db 100755 (executable)
@@ -16,7 +16,7 @@
 # this program; if not, write to the Free Software Foundation, Inc., 51
 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 
-import ast
+import t_def
 import element
 import sys
 import xml.etree.ElementTree
@@ -24,9 +24,9 @@ import y_tab
 
 sys.stdout.write('Enter the expression: ')
 sys.stdout.flush()
-_ast = y_tab.yyparse(ast.AST)
+_ast = y_tab.yyparse(t_def.AST)
 xml.etree.ElementTree.dump(_ast)
 element.serialize(_ast, 'a.xml', 'utf-8')
-_ast = element.deserialize('a.xml', ast.factory, 'utf-8')
+_ast = element.deserialize('a.xml', t_def.factory, 'utf-8')
 for i in _ast:
   sys.stdout.write('{0:g}\n'.format(i.eval()))
index acffdbf..e434a24 100644 (file)
  */
 
 %{
-import ast
+import t_def
 import element
 import y_tab
 %}
 
-DIGIT (?E{ast.AST.Num.Mantissa}[0-9]+)\.?|(?E{ast.AST.Num.Mantissa}[0-9]*)\.(?E{ast.AST.Num.Fraction}[0-9]+)
+DIGIT (?E{t_def.AST.Num.Mantissa}[0-9]+)\.?|(?E{t_def.AST.Num.Mantissa}[0-9]*)\.(?E{t_def.AST.Num.Fraction}[0-9]+)
 
 %%
 
 [ ]
-(?E{ast.AST.Num}{DIGIT})       {
+(?E{t_def.AST.Num}{DIGIT})     {
   y_tab.yylval = float(yytext)
   return y_tab.NUM
 }
diff --git a/tests_ast/cal_py.t b/tests_ast/cal_py.t
new file mode 100644 (file)
index 0000000..a3cd22f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 Nick Downing <nick@ndcode.org>
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+%{
+  import element
+%}
+
+%%
+
+class Text;
+class AST {
+  class Expr;
+  class Num: Expr {
+    class Mantissa: Text;
+    class Fraction: Text;
+  };
+  class Add;
+  class Sub;
+  class Mul;
+  class Div;
+  class Neg;
+};
+
+%%
+
+# Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+def factory(tag, attrib = {}, *args, **kwargs):
+  return tag_to_class.get(tag, element.Element)(tag, attrib, *args, **kwargs)
+
+@method(Text)
+def get_text(self):
+  return element.get_text(self, 0)
+del get_text
+
+@method(AST.Expr)
+def eval(self):
+  raise NotImplementedException()
+@method(AST.Num)
+def eval(self):
+  mantissa = self[0].get_text()
+  fraction = self[1].get_text() if len(self) >= 2 else ''
+  return int(mantissa + fraction) * 10 ** -len(fraction)
+@method(AST.Add)
+def eval(self):
+  return self[0].eval() + self[1].eval()
+@method(AST.Sub)
+def eval(self):
+  return self[0].eval() - self[1].eval()
+@method(AST.Mul)
+def eval(self):
+  return self[0].eval() * self[1].eval()
+@method(AST.Div)
+def eval(self):
+  return self[0].eval() / self[1].eval()
+@method(AST.Neg)
+def eval(self):
+  return -self[0].eval()
+del eval
index ed02b98..fbc503b 100644 (file)
@@ -17,7 +17,7 @@
  */
 
 %{
-import ast
+import t_def
 import sys
 %}
 %token NUM
@@ -38,22 +38,22 @@ S : S E '\n' {
   yyerrok()
 }
   ;
-E : %space (?E{ast.AST.Add}E '+' E) {
+E : %space (?E{t_def.AST.Add}E '+' E) {
     $$ = $1 + $3
   }
-  | %space (?E{ast.AST.Sub}E '-' E) {
+  | %space (?E{t_def.AST.Sub}E '-' E) {
     $$ = $1 - $3
   }
-  | %space (?E{ast.AST.Mul}E '*' E) {
+  | %space (?E{t_def.AST.Mul}E '*' E) {
     $$ = $1 * $3
   }
-  | %space (?E{ast.AST.Div}E '/' E) {
+  | %space (?E{t_def.AST.Div}E '/' E) {
     $$ = $1 / $3
   }
   | '(' E ')' {
     $$ = $2
   }
-  | %space (?E{ast.AST.Neg}'-' E) %prec UMINUS {
+  | %space (?E{t_def.AST.Neg}'-' E) %prec UMINUS {
     $$ = -$2
   }
   | NUM
index 2121e08..6de5806 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+# Copyright (C) 2019 Nick Downing <nick@ndcode.org>
 # SPDX-License-Identifier: GPL-2.0-only
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -58,15 +58,18 @@ def serialize_bool(value):
 
 str_to_bool = {'false': False, 'true': True}
 def deserialize_bool(text):
+  assert text is not None
   return str_to_bool[text]
 
 def serialize_int(value):
   return str(value)
 
 def deserialize_int(text):
+  assert text is not None
   return int(text)
 
 def serialize_ref(value, ref_list):
+  assert text is not None
   if value is None:
     ref = -1
   else:
@@ -83,6 +86,7 @@ def serialize_ref(value, ref_list):
   return str(ref)
 
 def deserialize_ref(text, ref_list):
+  assert text is not None
   ref = int(text)
   return None if ref < 0 else ref_list[ref]
 
@@ -90,6 +94,7 @@ def serialize_str(value):
   return value
 
 def deserialize_str(text):
+  assert text is not None
   return text
 
 def serialize(value, fout, encoding = 'unicode'):
@@ -160,6 +165,16 @@ def set_text(root, i, text):
   else:
     root[i - 1].tail = text
 
+def to_text(root):
+  return ''.join(
+    [
+      j
+      for i in range(len(root))
+      for j in [get_text(root, i), to_text(root[i])]
+    ] +
+    [get_text(root, len(root))]
+  )
+
 def concatenate(children, factory = Element, *args, **kwargs):
   root = factory(*args, **kwargs)
   for child in children: