Generate test input file and expected output file from existing ast.py projects
authorNick Downing <nick@ndcode.org>
Tue, 22 Jan 2019 03:34:41 +0000 (14:34 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 22 Jan 2019 03:34:41 +0000 (14:34 +1100)
.gitignore [new file with mode: 0644]
ast_to_pitree.py [new file with mode: 0755]
expected.sed [new file with mode: 0644]
expected.sh [new file with mode: 0755]
n.sh [new file with mode: 0755]
skel/skel_py.py [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..b2bf8c8
--- /dev/null
@@ -0,0 +1,3 @@
+__pycache__
+/out
+/tests
diff --git a/ast_to_pitree.py b/ast_to_pitree.py
new file mode 100755 (executable)
index 0000000..32bf5b8
--- /dev/null
@@ -0,0 +1,308 @@
+#!/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
+
+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)?'
+)
+re_method = re.compile(
+  '([\t ]+)def ([A-Za-z_][A-Za-z0-9_]*)\('
+)
+re_attribute = re.compile(
+  '([\t ]+)([A-Za-z_][A-Za-z0-9_]*) = (.*)'
+)
+re_comment = re.compile(
+  '([\t ]+)# (.*)'
+)
+stack = []
+classes = []
+base_classes = [{'element.Element': []}] # params
+method = {}
+
+sys.stdout.write('%{\n')
+had_blank = True
+
+lines = []
+delayed_line = ''
+
+line = sys.stdin.readline()
+while len(line):
+  match = re_class.match(line)
+  if match is not None:
+    indent = match.group(1)
+    class_name = match.group(2)
+    base_class = match.group(3)
+    if base_class is None:
+      base_class = ''
+    else:
+      base_class = base_class[1:-1]
+    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
+      temp_indent, _, _, _ = stack.pop()
+      if len(delayed_line):
+        lines.append('{0:s};\n'.format(delayed_line[:-3]))
+        delayed_line = ''
+      else:
+        lines.append('{0:s}}};\n'.format(temp_indent))
+    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({})
+    lines.append(delayed_line)
+    delayed_line = '{0:s}class {1:s}{2:s} {{\n'.format(
+      indent,
+      class_name,
+      ': ' + base_class if len(base_class) else ''
+    )
+    line = sys.stdin.readline()
+  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
+        temp_indent, _, _, _ = stack.pop()
+        if len(delayed_line):
+          lines.append('{0:s};\n'.format(delayed_line[:-3]))
+          delayed_line = ''
+        else:
+          lines.append('{0:s}}};\n'.format(temp_indent))
+      _, 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)
+
+      if len(fields):
+        lines.append(delayed_line)
+        delayed_line = ''
+        for type, name in fields:
+          lines.append(
+            '{0:s}{1:s} {2:s} = {3:s};\n'.format(
+              indent,
+              type,
+              name,
+              default_value[type]
+            )
+          )
+
+      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
+      line = sys.stdin.readline()
+    else:
+      match = re_factory.match(line)
+      if match is not None:
+        indent = '  ' #match.group(1)
+        param = match.group(2)
+        begin = match.group(3)
+
+        if not had_blank:
+          sys.stdout.write('\n')
+        sys.stdout.write(
+          '''{0:s}def factory(tag, attrib = {{}}, *args, **kwargs):
+{1:s}  return tag_to_class.get(tag, {2:s})(tag, attrib, *args, **kwargs)
+'''.format(
+            indent,
+            indent,
+            param,
+            indent
+          )
+        )
+        had_blank = True
+
+        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
+        line = sys.stdin.readline()
+      else: 
+        match = re_method.match(line)
+        if match is not None:
+          indent = match.group(1)
+          name = match.group(2)
+
+          if name not in method:
+            method[name] = []
+          method[name].extend(
+            [
+              '@method({0:s})\n'.format('.'.join([i for _, i, _, _ in stack])),
+              line[len(indent):]
+            ]
+          )
+          line = sys.stdin.readline()
+          line_rstrip = line.rstrip()
+          while (
+            len(line) and
+            line_rstrip[:len(indent)] == indent[:len(line_rstrip)] and
+            (len(line_rstrip) <= len(indent) or line[len(indent)] in '\t )')
+          ):
+            method[name].append(line[len(indent):])
+            line = sys.stdin.readline()
+            line_rstrip = line.rstrip()
+        else:
+          match = re_attribute.match(line)
+          if match is not None:
+            indent = match.group(1)
+            name = match.group(2)
+            value = match.group(3)
+
+            if name not in method:
+              method[name] = []
+            method[name].append(
+              '{0:s}.{1:s} = {2:s}\n'.format(
+                '.'.join([i for _, i, _, _ in stack]),
+                name,
+                value
+              )
+            )
+            line = sys.stdin.readline()
+          else:
+            match = re_comment.match(line)
+            if match is not None:
+              indent = match.group(1)
+              text = match.group(2)
+              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
+                temp_indent, _, _, _ = stack.pop()
+                if len(delayed_line):
+                  lines.append('{0:s};\n'.format(delayed_line[:-3]))
+                  delayed_line = ''
+                else:
+                  lines.append('{0:s}}};\n'.format(temp_indent))
+
+              lines.append(delayed_line)
+              delayed_line = ''
+              lines.append('{0:s}/* {1:s} */\n'.format(indent, text))
+
+              line = sys.stdin.readline()
+            else:
+              line_rstrip = line.rstrip()
+              if len(line_rstrip):
+                sys.stdout.write('  {0:s}\n'.format(line_rstrip))
+                had_blank = False
+              elif not had_blank:
+                sys.stdout.write('\n')
+                had_blank = True
+              line = sys.stdin.readline()
+
+while len(stack):
+  #_ , 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
+  temp_indent, _, _, _ = stack.pop()
+  if len(delayed_line):
+    lines.append('{0:s};\n'.format(delayed_line[:-3]))
+    delayed_line = ''
+  else:
+    lines.append('{0:s}}};\n'.format(temp_indent))
+lines.append(delayed_line)
+
+sys.stdout.write(
+  '%}}\n\n%%\n\n{0:s}\n%%\n{1:s}'.format(
+    ''.join(lines),
+    ''.join(
+      [
+        '\n{0:s}{1:s}'.format(
+          ''.join(lines),
+          (
+            'del {0:s}\n'.format(name)
+          if any([i[:1] == '@' for i in lines]) else
+            ''
+          )
+        )
+        for name, lines in method.items()
+      ]
+    )
+  )
+)
diff --git a/expected.sed b/expected.sed
new file mode 100644 (file)
index 0000000..90b0f63
--- /dev/null
@@ -0,0 +1,3 @@
+/^ *class/p
+/^ *# GENERATE ELEMENT/,/^ *# GENERATE END/p
+/# GENERATE FACTORY/,/# GENERATE END/{s/^def factory/#&/; s/^  return tag_to_class/#&/; p}
diff --git a/expected.sh b/expected.sh
new file mode 100755 (executable)
index 0000000..1f33308
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+cat <<EOF
+# GENERATE SECTION1 BEGIN
+EOF
+sed -ne '/^%{/,/%}/p' <$2 |sed -ne '/^$/p; s/^  //p'
+cat <<EOF
+# GENERATE END
+
+# GENERATE SECTION2 BEGIN
+EOF
+sed -nf expected.sed <$1 |grep -v '^ *#'
+cat <<EOF
+# GENERATE END
+
+def method(_class):
+  def decorator(func):
+    setattr(_class, func.__name__, func)
+    return func
+  return decorator
+
+# GENERATE SECTION3 BEGIN
+EOF
+sed -ne '/^%%/,$p' <$2 |sed -ne '2,$p' |sed -ne '/^%%/,$p' |sed -ne '2,$p'
+cat <<EOF
+# GENERATE END
+EOF
diff --git a/n.sh b/n.sh
new file mode 100755 (executable)
index 0000000..3576b91
--- /dev/null
+++ b/n.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+if ! test -d tests
+then
+  mkdir tests
+  ./ast_to_pitree.py <../c_to_python.git/ast.py >tests/ansi_c.t
+  ./ast_to_pitree.py <../pilex.git/ast.py >tests/pilex.t
+  ./ast_to_pitree.py <../piyacc.git/ast.py >tests/piyacc.t
+  ./ast_to_pitree.py <../piyacc.git/tests_ast/ast.py >tests/cal.t
+fi
+if ! test -d out
+then
+  mkdir out
+  ./expected.sh ../c_to_python.git/ast.py tests/ansi_c.t >out/ast_ansi_c.py.ok
+  ./expected.sh ../pilex.git/ast.py tests/pilex.t >out/ast_pilex.py.ok
+  ./expected.sh ../piyacc.git/ast.py tests/piyacc.t >out/ast_piyacc.py.ok
+  ./expected.sh ../piyacc.git/tests_ast/ast.py tests/cal.t >out/ast_cal.py.ok
+fi
diff --git a/skel/skel_py.py b/skel/skel_py.py
new file mode 100644 (file)
index 0000000..46f124c
--- /dev/null
@@ -0,0 +1,11 @@
+# GENERATE SECTION1
+
+# GENERATE SECTION2
+
+def method(_class):
+  def decorator(func):
+    setattr(_class, func.__name__, func)
+    return func
+  return decorator
+
+# GENERATE SECTION3