From 6ddeeacd24134ae1e6729c82e90ca49153d6b5f4 Mon Sep 17 00:00:00 2001
From: umarcor <unai.martinezcorral@ehu.eus>
Date: Mon, 28 Dec 2020 20:35:32 +0100
Subject: scripts: move 'pyGHDL/xtools' to 'scripts', update Makefiles and docs
 accordingly

---
 Makefile.in               |   2 +-
 README.md                 |   2 +-
 configure                 |   2 +-
 doc/internals/AST.rst     |   2 +-
 pyGHDL/xtools/pnodes.py   | 988 ----------------------------------------------
 pyGHDL/xtools/pnodespy.py | 263 ------------
 scripts/pnodes.py         | 988 ++++++++++++++++++++++++++++++++++++++++++++++
 scripts/pnodespy.py       | 263 ++++++++++++
 src/edif/Makefile         |  12 +-
 src/psl/Makefile          |  11 +-
 src/vhdl/Makefile         |  35 +-
 11 files changed, 1294 insertions(+), 1274 deletions(-)
 delete mode 100755 pyGHDL/xtools/pnodes.py
 delete mode 100755 pyGHDL/xtools/pnodespy.py
 create mode 100755 scripts/pnodes.py
 create mode 100755 scripts/pnodespy.py

diff --git a/Makefile.in b/Makefile.in
index 1ef92ac8d..c089789d5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -457,7 +457,7 @@ uninstall.libghdl:
 
 libghdl-py.tgz:
 	[ -d pythonb ] || $(MKDIR) pythonb
-	$(CP) -r $(srcdir)/python/libghdl $(srcdir)/python/setup.py pythonb
+	$(CP) -r $(srcdir)/pyGHDL/libghdl $(srcdir)/setup.py pythonb
 	tar -zcvf $@ -C pythonb .
 
 ################ ghwdump #################################################
diff --git a/README.md b/README.md
index becdbf3bf..ae032c91d 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ That's all!
 
 - `libghdl` is a shared library that includes a subset of the regular features plus some features to be used by extension tools (i.e. `libghdl-py`). This is built along with the regular GHDL and it supports both non-synthesisable and synthesisable code. Nonetheless, this is not for users, but for tools built on top of the core. When configured along with `--enable-synth`, this shared library includes **[experimental]** synthesis features too.
 
-- [libghdl-py](python/libghdl) is a Python interface to `libghdl`. Currently, it is only used by `ghdl-ls`; however, it can be useful for advanced users which are willing to build Python utilities based on GHDL.
+- [pyGHDL](pyGHDL) is a Python interface to `libghdl`. Currently, it is only used by `ghdl-ls`; however, it can be useful for advanced users which are willing to build Python utilities based on GHDL.
 
 - **[experimental]** [ghdl-yosys-plugin](https://github.com/ghdl/ghdl-yosys-plugin) is the integration of GHDL as a frontend plugin module for [Yosys Open SYnthesis Suite](http://www.clifford.at/yosys/), which uses the `libghdl` library (built with `--enable-synth`).
 
diff --git a/configure b/configure
index 83d9b2628..75599d776 100755
--- a/configure
+++ b/configure
@@ -190,7 +190,7 @@ fi
 
 # Check the version of libghdl is correct.
 if [ "$enable_libghdl" = true ]; then
-    libghdl_version="$srcdir/python/libghdl/version.py"
+    libghdl_version="$srcdir/pyGHDL/libghdl/version.py"
     # Extract content between single quotes in version.py, to avoid false positives due to LF/CRLF mismatch.
     if [ "$ghdl_version" != "`sed 's/.*"\(.*\)".*/\1/g' $libghdl_version`" ]; then
       echo "Sorry, the version of $libghdl_version is not correct"
diff --git a/doc/internals/AST.rst b/doc/internals/AST.rst
index 488fa6d71..ad52f0286 100644
--- a/doc/internals/AST.rst
+++ b/doc/internals/AST.rst
@@ -75,7 +75,7 @@ either a node reference, a boolean flag or a enumerated type (like
 node or to a list.
 
 The accessors for the node are generated automatically by the python
-script :file:`src/xtools/pnodes.py`.
+script :file:`src/scripts/pnodes.py`.
 
 Why a meta-model ?
 ******************
diff --git a/pyGHDL/xtools/pnodes.py b/pyGHDL/xtools/pnodes.py
deleted file mode 100755
index 793c1c712..000000000
--- a/pyGHDL/xtools/pnodes.py
+++ /dev/null
@@ -1,988 +0,0 @@
-#!/usr/bin/env python
-
-import re
-import sys
-import argparse
-
-field_file = "nodes.ads"
-kind_file = "iirs.ads"
-node_file = "iirs.ads"
-template_file = "iirs.adb.in"
-meta_base_file = "nodes_meta"
-prefix_name = "Iir_Kind_"
-prefix_range_name = "Iir_Kinds_"
-type_name = "Iir_Kind"
-node_type = "Iir"
-conversions = ["uc", "pos", "grp"]
-
-
-class FuncDesc:
-    def __init__(self, name, fields, conv, acc, pname, ptype, rname, rtype):
-        self.name = name
-        self.fields = fields  # List of physical fields used
-        self.conv = conv
-        self.acc = acc  # access: Chain, Chain_Next, Ref, Of_Ref, Maybe_Ref,
-        #                 Forward_Ref, Maybe_Forward_Ref
-        self.pname = pname  # Parameter mame
-        self.ptype = ptype  # Parameter type
-        self.rname = rname  # value name (for procedure)
-        self.rtype = rtype  # value type
-
-
-class NodeDesc:
-    def __init__(self, name, format, fields, attrs):
-        self.name = name
-        self.format = format
-        self.fields = fields  # {field: FuncDesc} dict, defined for all fields
-        self.attrs = attrs  # A {attr: FuncDesc} dict
-        self.order = []  # List of fields name, in order of appearance.
-
-
-class line:
-    def __init__(self, string, no):
-        self.l = string
-        self.n = no
-
-
-class EndOfFile(Exception):
-    def __init__(self, filename):
-        self.filename = filename
-
-    def __str__(self):
-        return "end of file " + self.filename
-
-
-class linereader:
-    def __init__(self, filename):
-        self.filename = filename
-        self.f = open(filename)
-        self.lineno = 0
-        self.l = ""
-
-    def get(self):
-        self.l = self.f.readline()
-        if not self.l:
-            raise EndOfFile(self.filename)
-        self.lineno = self.lineno + 1
-        return self.l
-
-
-class ParseError(Exception):
-    def __init__(self, lr, msg):
-        self.lr = lr
-        self.msg = msg
-
-    def __str__(self):
-        return "Error: " + self.msg
-        return (
-            "Parse error at " + self.lr.filname + ":" + self.lr.lineno + ": " + self.msg
-        )
-
-
-# Return fields description.
-# This is a dictionary.  The keys represent the possible format of a node.
-# The values are dictionnaries representing fields.  Keys are fields name, and
-# values are fields type.
-def read_fields(file):
-    fields = {}
-    formats = []
-    lr = linereader(file)
-
-    #  Search for 'type Format_Type is'
-    while lr.get() != "   type Format_Type is\n":
-        pass
-
-    # Skip '('
-    if lr.get() != "     (\n":
-        raise "no open parenthesis after Format_Type"
-
-    # Read formats
-    l = lr.get()
-    pat_field_name = re.compile("      Format_(\w+),?\n")
-    while l != "     );\n":
-        m = pat_field_name.match(l)
-        if m is None:
-            print l
-            raise "bad literal within Format_Type"
-        name = m.group(1)
-        formats.append(name)
-        fields[name] = {}
-        l = lr.get()
-
-    # Read fields
-    l = lr.get()
-    pat_fields = re.compile("   -- Fields of Format_(\w+):\n")
-    pat_field_desc = re.compile("   --   (\w+) : (\w+).*\n")
-    format_name = ""
-    common_desc = {}
-
-    # Read until common fields.
-    while l != "   -- Common fields are:\n":
-        l = lr.get()
-    format_name = "Common"
-    nbr_formats = 0
-
-    while True:
-        # 1) Read field description
-        l = lr.get()
-        desc = common_desc.copy()
-        while True:
-            m = pat_field_desc.match(l)
-            if m is None:
-                break
-            desc[m.group(1)] = m.group(2)
-            l = lr.get()
-            # print 'For: ' + format_name + ': ' + m.group(1)
-
-        # 2) Disp
-        if format_name == "Common":
-            common_desc = desc
-        else:
-            fields[format_name] = desc
-
-        # 3) Read next format
-        if l == "\n":
-            if nbr_formats == len(fields):
-                break
-            else:
-                l = lr.get()
-
-        # One for a format
-        m = pat_fields.match(l)
-        if m is not None:
-            format_name = m.group(1)
-            if format_name not in fields:
-                raise ParseError(lr, "Format " + format_name + " is unknown")
-            nbr_formats = nbr_formats + 1
-        else:
-            raise ParseError(lr, "unhandled format line")
-
-    return (formats, fields)
-
-
-# Read kinds and kinds ranges.
-def read_kinds(filename):
-    lr = linereader(filename)
-    kinds = []
-    #  Search for 'type Iir_Kind is'
-    while lr.get() != "   type " + type_name + " is\n":
-        pass
-    # Skip '('
-    if lr.get() != "     (\n":
-        raise ParseError(lr, 'no open parenthesis after "type ' + type_name + '"')
-
-    # Read literals
-    pat_node = re.compile("      " + prefix_name + "(\w+),?( +-- .*)?\n")
-    pat_comment = re.compile("( +-- .*)?\n")
-    while True:
-        l = lr.get()
-        if l == "     );\n":
-            break
-        m = pat_node.match(l)
-        if m:
-            kinds.append(m.group(1))
-            continue
-        m = pat_comment.match(l)
-        if not m:
-            raise ParseError(lr, "Unknown line within kind declaration")
-
-    # Check subtypes
-    pat_subtype = re.compile("   subtype " + r"(\w+) is " + type_name + " range\n")
-    pat_first = re.compile("     " + prefix_name + r"(\w+) ..\n")
-    pat_last = re.compile("     " + prefix_name + r"(\w+);\n")
-    pat_middle = re.compile("   --" + prefix_name + r"(\w+)\n")
-    kinds_ranges = {}
-    while True:
-        l = lr.get()
-        # Start of methods is also end of subtypes.
-        if l == "   -- General methods.\n":
-            break
-        # Found a subtype.
-        m = pat_subtype.match(l)
-        if m:
-            # Check first bound
-            name = m.group(1)
-            if not name.startswith(prefix_range_name):
-                raise ParseError(lr, "incorrect prefix for subtype")
-            name = name[len(prefix_range_name) :]
-            l = lr.get()
-            mf = pat_first.match(l)
-            if not mf:
-                raise ParseError(lr, "badly formated first bound of subtype")
-            first = kinds.index(mf.group(1))
-            idx = first
-            has_middle = None
-            # Read until last bound
-            while True:
-                l = lr.get()
-                ml = pat_middle.match(l)
-                if ml:
-                    # Check element in the middle
-                    n = ml.group(1)
-                    if n not in kinds:
-                        raise ParseError(lr, "unknown kind " + n + " in subtype")
-                    if kinds.index(n) != idx + 1:
-                        raise ParseError(
-                            lr, "missing " + kinds[idx + 1] + " in subtype"
-                        )
-                    has_middle = True
-                    idx = idx + 1
-                else:
-                    # Check last bound
-                    ml = pat_last.match(l)
-                    if ml:
-                        last = kinds.index(ml.group(1))
-                        if last != idx + 1 and has_middle:
-                            raise ParseError(
-                                lr, "missing " + kinds[idx] + " in subtype"
-                            )
-                        break
-                    raise ParseError(lr, "unhandled line in subtype")
-            kinds_ranges[name] = kinds[first : last + 1]
-    return (kinds, kinds_ranges)
-
-
-# Read functions
-def read_methods(filename):
-    lr = linereader(filename)
-    funcs = []
-    pat_field = re.compile(r"   --  Field: ([\w,]+)( \w+)?( \(\w+\))?\n")
-    pat_conv = re.compile(r"^ \((\w+)\)$")
-    pat_func = re.compile(r"   function Get_(\w+) \((\w+) : (\w+)\) return (\w+);\n")
-    pat_proc = re.compile(r"   procedure Set_(\w+) \((\w+) : (\w+); (\w+) : (\w+)\);\n")
-    pat_end = re.compile("end [A-Za-z.]+;\n")
-    while True:
-        l = lr.get()
-        # Start of methods
-        if l == "   -- General methods.\n":
-            break
-    while True:
-        l = lr.get()
-        if pat_end.match(l):
-            break
-        m = pat_field.match(l)
-        if m:
-            fields = m.group(1).split(",")
-            # Extract access modifier
-            acc = m.group(2)
-            if acc:
-                acc = acc.strip()
-            # Extract conversion
-            conv = m.group(3)
-            if conv:
-                mc = pat_conv.match(conv)
-                if not mc:
-                    raise ParseError(lr, "conversion ill formed")
-                conv = mc.group(1)
-                if conv not in conversions:
-                    raise ParseError(lr, "unknown conversion " + conv)
-            else:
-                conv = None
-            if len(fields) > 1 and conv != "grp":
-                raise ParseError(lr, "bad conversion for multiple fields")
-            # Read function
-            l = lr.get()
-            mf = pat_func.match(l)
-            if not mf:
-                raise ParseError(lr, "function declaration expected after Field")
-            # Read procedure
-            l = lr.get()
-            mp = pat_proc.match(l)
-            if not mp:
-                raise ParseError(lr, "procedure declaration expected after function")
-            # Consistency check between function and procedure
-            if mf.group(1) != mp.group(1):
-                raise ParseError(lr, "function and procedure name mismatch")
-            if mf.group(2) != mp.group(2):
-                raise ParseError(lr, "parameter name mismatch with function")
-            if mf.group(3) != mp.group(3):
-                raise ParseError(lr, "parameter type mismatch with function")
-            if mf.group(4) != mp.group(5):
-                raise ParseError(lr, "result type mismatch with function")
-            funcs.append(
-                FuncDesc(
-                    mf.group(1),
-                    fields,
-                    conv,
-                    acc,
-                    mp.group(2),
-                    mp.group(3),
-                    mp.group(4),
-                    mp.group(5),
-                )
-            )
-
-    return funcs
-
-
-# Read description for one node
-# LR is the line reader.  NAMES is the list of (node name, format)
-#  (one description may describe several nodes).
-# A comment start at column 2 or 4 or later.
-def read_nodes_fields(lr, names, fields, nodes, funcs_dict):
-    pat_only = re.compile("   -- Only for " + prefix_name + "(\w+):\n")
-    pat_only_bad = re.compile("   -- *Only for.*\n")
-    pat_field = re.compile("   --   Get/Set_(\w+) \((Alias )?([\w,]+)\)\n")
-    pat_comment = re.compile("   --(|  [^ ].*|    .*)\n")
-
-    # Create nodes
-    cur_nodes = []
-    for (nm, fmt) in names:
-        if fmt not in fields:
-            raise ParseError(lr, 'unknown format "{}"'.format(fmt))
-        n = NodeDesc(nm, fmt, {x: None for x in fields[fmt]}, {})
-        nodes[nm] = n
-        cur_nodes.append(n)
-
-    # Skip comments
-    l = lr.l
-    while pat_comment.match(l):
-        l = lr.get()
-
-    # Look for fields
-    while l != "\n":
-        # Skip comments
-        while pat_comment.match(l):
-            l = lr.get()
-
-        # Handle 'Only ...'
-        m = pat_only.match(l)
-        if m:
-            only_nodes = []
-            while True:
-                name = m.group(1)
-                n = nodes.get(name, None)
-                if n is None:
-                    raise ParseError(lr, "node is unknown")
-                if n not in cur_nodes:
-                    raise ParseError(lr, "node not currently described")
-                only_nodes.append(n)
-                l = lr.get()
-                m = pat_only.match(l)
-                if not m:
-                    break
-        else:
-            # By default a field applies to all nodes.
-            only_nodes = cur_nodes
-
-        # Skip comments
-        while pat_comment.match(l):
-            l = lr.get()
-
-        # Handle field: '--  Get/Set_FUNC (Alias? FIELD)'
-        m = pat_field.match(l)
-        if not m:
-            if pat_only_bad.match(l):
-                raise ParseError(lr, "misleading 'Only for' comment")
-            else:
-                raise ParseError(lr, "bad line in node description")
-
-        func = m.group(1)
-        alias = m.group(2)
-        fields = m.group(3).split(",")
-
-        # Check the function exists and if the field is correct.
-        if func not in funcs_dict:
-            raise ParseError(lr, "unknown function")
-        func = funcs_dict[func]
-        if func.fields != fields:
-            raise ParseError(lr, "fields mismatch")
-
-        for c in only_nodes:
-            for f in fields:
-                if f not in c.fields:
-                    raise ParseError(lr, "field " + f + " does not exist in node")
-            if not alias:
-                for f in fields:
-                    if c.fields[f]:
-                        raise ParseError(lr, "field " + f + " already used")
-                    c.fields[f] = func
-                    c.order.append(f)
-            c.attrs[func.name] = func
-
-        l = lr.get()
-
-
-def read_nodes(filename, kinds, kinds_ranges, fields, funcs):
-    """Read description for all nodes."""
-    lr = linereader(filename)
-    funcs_dict = {x.name: x for x in funcs}
-    nodes = {}
-
-    # Skip until start
-    while lr.get() != "   -- Start of " + type_name + ".\n":
-        pass
-
-    pat_decl = re.compile("   -- " + prefix_name + "(\w+) \((\w+)\)\n")
-    pat_decls = re.compile("   -- " + prefix_range_name + "(\w+) \((\w+)\)\n")
-    pat_comment_line = re.compile("   --+\n")
-    pat_comment_box = re.compile("   --(  .*)?\n")
-    while True:
-        l = lr.get()
-        if l == "   -- End of " + type_name + ".\n":
-            break
-        if l == "\n":
-            continue
-        m = pat_decl.match(l)
-        if m:
-            # List of nodes being described by the current description.
-            names = []
-
-            # Declaration of the first node
-            while True:
-                name = m.group(1)
-                if name not in kinds:
-                    raise ParseError(lr, "unknown node")
-                fmt = m.group(2)
-                names.append((name, fmt))
-                if name in nodes:
-                    raise ParseError(lr, "node {} already described".format(name))
-                # There might be several nodes described at once.
-                l = lr.get()
-                m = pat_decl.match(l)
-                if not m:
-                    break
-            read_nodes_fields(lr, names, fields, nodes, funcs_dict)
-            continue
-        m = pat_decls.match(l)
-        if m:
-            # List of nodes being described by the current description.
-            name = m.group(1)
-            fmt = m.group(2)
-            names = [(k, fmt) for k in kinds_ranges[name]]
-            l = lr.get()
-            read_nodes_fields(lr, names, fields, nodes, funcs_dict)
-            continue
-        if pat_comment_line.match(l) or pat_comment_box.match(l):
-            continue
-        raise ParseError(lr, "bad line in node description")
-
-    for k in kinds:
-        if k not in nodes:
-            raise ParseError(lr, 'no description for "{}"'.format(k))
-    return nodes
-
-
-def gen_choices(choices):
-    """Generate a choice 'when A | B ... Z =>' using elements of CHOICES."""
-    is_first = True
-    for c in choices:
-        if is_first:
-            print "        ",
-            print "when",
-        else:
-            print
-            print "        ",
-            print "  |",
-        print prefix_name + c,
-        is_first = False
-    print "=>"
-
-
-def gen_get_format(formats, nodes, kinds):
-    """Generate the Get_Format function."""
-    print "   function Get_Format (Kind : " + type_name + ") " + "return Format_Type is"
-    print "   begin"
-    print "      case Kind is"
-    for f in formats:
-        choices = [k for k in kinds if nodes[k].format == f]
-        gen_choices(choices)
-        print "            return Format_" + f + ";"
-    print "      end case;"
-    print "   end Get_Format;"
-
-
-def gen_subprg_header(decl):
-    if len(decl) < 76:
-        print decl + " is"
-    else:
-        print decl
-        print "   is"
-    print "   begin"
-
-
-def gen_assert(func):
-    print "      pragma Assert (" + func.pname + " /= Null_" + node_type + ");"
-    cond = "(Has_" + func.name + " (Get_Kind (" + func.pname + ")),"
-    msg = '"no field ' + func.name + '");'
-    if len(cond) < 60:
-        print "      pragma Assert " + cond
-        print "                     " + msg
-    else:
-        print "      pragma Assert"
-        print "         " + cond
-        print "          " + msg
-
-
-def get_field_type(fields, f):
-    for fld in fields.values():
-        if f in fld:
-            return fld[f]
-    return None
-
-
-def gen_get_set(func, nodes, fields):
-    """Generate Get_XXX/Set_XXX subprograms for FUNC."""
-    rtype = func.rtype
-    # If the function needs several fields, it must be user defined
-    if func.conv == "grp":
-        print "   type %s_Conv is record" % rtype
-        for f in func.fields:
-            print "      %s: %s;" % (f, get_field_type(fields, f))
-        print "   end record;"
-        print "   pragma Pack (%s_Conv);" % rtype
-        print "   pragma Assert (%s_Conv'Size = %s'Size);" % (rtype, rtype)
-        print
-    else:
-        f = func.fields[0]
-        g = "Get_" + f + " (" + func.pname + ")"
-
-    s = func.rname
-    if func.conv:
-        if func.conv == "uc":
-            field_type = get_field_type(fields, f)
-            g = field_type + "_To_" + rtype + " (" + g + ")"
-            s = rtype + "_To_" + field_type + " (" + s + ")"
-        elif func.conv == "pos":
-            g = rtype + "'Val (" + g + ")"
-            s = rtype + "'Pos (" + s + ")"
-
-    subprg = (
-        "   function Get_"
-        + func.name
-        + " ("
-        + func.pname
-        + " : "
-        + func.ptype
-        + ") return "
-        + rtype
-    )
-    if func.conv == "grp":
-        print subprg
-        print "   is"
-        print "      function To_%s is new Ada.Unchecked_Conversion" % func.rtype
-        print "         (%s_Conv, %s);" % (rtype, rtype)
-        print "      Conv : %s_Conv;" % rtype
-        print "   begin"
-    else:
-        gen_subprg_header(subprg)
-    gen_assert(func)
-    if func.conv == "grp":
-        for f in func.fields:
-            print "      Conv.%s := Get_%s (%s);" % (f, f, func.pname)
-        g = "To_%s (Conv)" % rtype
-    print "      return " + g + ";"
-    print "   end Get_" + func.name + ";"
-    print
-
-    subprg = (
-        "   procedure Set_"
-        + func.name
-        + " ("
-        + func.pname
-        + " : "
-        + func.ptype
-        + "; "
-        + func.rname
-        + " : "
-        + func.rtype
-        + ")"
-    )
-    if func.conv == "grp":
-        print subprg
-        print "   is"
-        print "      function To_%s_Conv is new Ada.Unchecked_Conversion" % func.rtype
-        print "         (%s, %s_Conv);" % (rtype, rtype)
-        print "      Conv : %s_Conv;" % rtype
-        print "   begin"
-    else:
-        gen_subprg_header(subprg)
-    gen_assert(func)
-    if func.conv == "grp":
-        print "      Conv := To_%s_Conv (%s);" % (rtype, func.rname)
-        for f in func.fields:
-            print "      Set_%s (%s, Conv.%s);" % (f, func.pname, f)
-    else:
-        print "      Set_" + f + " (" + func.pname + ", " + s + ");"
-    print "   end Set_" + func.name + ";"
-    print
-
-
-def funcs_of_node(n):
-    return sorted([fv.name for fv in n.fields.values() if fv])
-
-
-def gen_has_func_spec(name, suff):
-    spec = "   function Has_" + name + " (K : " + type_name + ")"
-    ret = " return Boolean" + suff
-    if len(spec) < 60:
-        print spec + ret
-    else:
-        print spec
-        print "     " + ret
-
-
-def do_disp_formats():
-    for fmt in fields:
-        print "Fields of Format_" + fmt
-        fld = fields[fmt]
-        for k in fld:
-            print "  " + k + " (" + fld[k] + ")"
-
-
-def do_disp_kinds():
-    print "Kinds are:"
-    for k in kinds:
-        print "  " + prefix_name + k
-
-
-def do_disp_funcs():
-    print "Functions are:"
-    for f in funcs:
-        s = "{0} ({1}: {2}".format(f.name, f.field, f.rtype)
-        if f.acc:
-            s += " acc:" + f.acc
-        if f.conv:
-            s += " conv:" + f.conv
-        s += ")"
-        print s
-
-
-def do_disp_types():
-    print "Types are:"
-    s = set([])
-    for f in funcs:
-        s |= set([f.rtype])
-    for t in sorted(s):
-        print "  " + t
-
-
-def do_disp_nodes():
-    for k in kinds:
-        v = nodes[k]
-        print prefix_name + k + " (" + v.format + ")"
-        flds = [fk for fk, fv in v.fields.items() if fv]
-        for fk in sorted(flds):
-            print "  " + fk + ": " + v.fields[fk].name
-
-
-def do_get_format():
-    gen_get_format(formats, nodes)
-
-
-def do_body():
-    lr = linereader(template_file)
-    while True:
-        l = lr.get().rstrip()
-        print l
-        if l == "   --  Subprograms":
-            gen_get_format(formats, nodes, kinds)
-            print
-            for f in funcs:
-                gen_get_set(f, nodes, fields)
-        if l[0:3] == "end":
-            break
-
-
-def get_types():
-    s = set([])
-    for f in funcs:
-        s |= set([f.rtype])
-    return [t for t in sorted(s)]
-
-
-def get_attributes():
-    s = set([])
-    for f in funcs:
-        if f.acc:
-            s |= set([f.acc])
-    res = [t for t in sorted(s)]
-    res.insert(0, "None")
-    return res
-
-
-def gen_enum(prefix, vals):
-    last = None
-    for v in vals:
-        if last:
-            print last + ","
-        last = prefix + v
-    print last
-
-
-def do_meta_specs():
-    lr = linereader(meta_base_file + ".ads.in")
-    types = get_types()
-    while True:
-        l = lr.get().rstrip()
-        if l == "      --  TYPES":
-            gen_enum("      Type_", types)
-        elif l == "      --  FIELDS":
-            gen_enum("      Field_", [f.name for f in funcs])
-        elif l == "      --  ATTRS":
-            gen_enum("      Attr_", get_attributes())
-        elif l == "   --  FUNCS":
-            for t in types:
-                print "   function Get_" + t
-                print "      (N : " + node_type + "; F : Fields_Enum) return " + t + ";"
-                print "   procedure Set_" + t
-                print "      (N : " + node_type + "; F : Fields_Enum; V: " + t + ");"
-                print
-            for f in funcs:
-                gen_has_func_spec(f.name, ";")
-        elif l[0:3] == "end":
-            print l
-            break
-        else:
-            print l
-
-
-def do_meta_body():
-    lr = linereader(meta_base_file + ".adb.in")
-    while True:
-        l = lr.get().rstrip()
-        if l == "      --  FIELDS_TYPE":
-            last = None
-            for f in funcs:
-                if last:
-                    print last + ","
-                last = "      Field_" + f.name + " => Type_" + f.rtype
-            print last
-        elif l == "         --  FIELD_IMAGE":
-            for f in funcs:
-                print "         when Field_" + f.name + " =>"
-                print '            return "' + f.name.lower() + '";'
-        elif l == "         --  IIR_IMAGE":
-            for k in kinds:
-                print "         when " + prefix_name + k + " =>"
-                print '            return "' + k.lower() + '";'
-        elif l == "         --  FIELD_ATTRIBUTE":
-            for f in funcs:
-                print "         when Field_" + f.name + " =>"
-                if f.acc:
-                    attr = f.acc
-                else:
-                    attr = "None"
-                print "            return Attr_" + attr + ";"
-        elif l == "      --  FIELDS_ARRAY":
-            last = None
-            nodes_types = [node_type, node_type + "_List", node_type + "_Flist"]
-            for k in kinds:
-                v = nodes[k]
-                if last:
-                    print last + ","
-                last = None
-                print "      --  " + prefix_name + k
-                # Get list of physical fields for V, in some order.
-                if flag_keep_order:
-                    flds = v.order
-                else:
-                    # First non Iir and no Iir_List.
-                    flds = sorted(
-                        [
-                            fk
-                            for fk, fv in v.fields.items()
-                            if fv and fv.rtype not in nodes_types
-                        ]
-                    )
-                    # Then Iir and Iir_List in order of appearance
-                    flds += (fv for fv in v.order if v.fields[fv].rtype in nodes_types)
-                # Print the corresponding node field, but remove duplicate due
-                # to 'grp'.
-                fldsn = []
-                for fk in flds:
-                    if last:
-                        print last + ","
-                    # Remove duplicate
-                    fn = v.fields[fk].name
-                    if fn not in fldsn:
-                        last = "      Field_" + fn
-                        fldsn.append(fn)
-                    else:
-                        last = None
-            if last:
-                print last
-        elif l == "      --  FIELDS_ARRAY_POS":
-            pos = -1
-            last = None
-            for k in kinds:
-                v = nodes[k]
-                # Create a set to remove duplicate for 'grp'.
-                flds = set([fv.name for fk, fv in v.fields.items() if fv])
-                pos += len(flds)
-                if last:
-                    print last + ","
-                last = "      " + prefix_name + k + " => {}".format(pos)
-            print last
-        elif l == "   --  FUNCS_BODY":
-            # Build list of types
-            s = set([])
-            for f in funcs:
-                s |= set([f.rtype])
-            types = [t for t in sorted(s)]
-            for t in types:
-                print "   function Get_" + t
-                print "      (N : " + node_type + "; F : Fields_Enum) return " + t + " is"
-                print "   begin"
-                print "      pragma Assert (Fields_Type (F) = Type_" + t + ");"
-                print "      case F is"
-                for f in funcs:
-                    if f.rtype == t:
-                        print "         when Field_" + f.name + " =>"
-                        print "            return Get_" + f.name + " (N);"
-                print "         when others =>"
-                print "            raise Internal_Error;"
-                print "      end case;"
-                print "   end Get_" + t + ";"
-                print
-                print "   procedure Set_" + t
-                print "      (N : " + node_type + "; F : Fields_Enum; V: " + t + ") is"
-                print "   begin"
-                print "      pragma Assert (Fields_Type (F) = Type_" + t + ");"
-                print "      case F is"
-                for f in funcs:
-                    if f.rtype == t:
-                        print "         when Field_" + f.name + " =>"
-                        print "            Set_" + f.name + " (N, V);"
-                print "         when others =>"
-                print "            raise Internal_Error;"
-                print "      end case;"
-                print "   end Set_" + t + ";"
-                print
-            for f in funcs:
-                gen_has_func_spec(f.name, " is")
-                choices = [k for k in kinds if f.name in nodes[k].attrs]
-                if len(choices) == 0:
-                    print "      pragma Unreferenced (K);"
-                print "   begin"
-                if len(choices) == 0:
-                    print "      return False;"
-                elif len(choices) == 1:
-                    print "      return K = " + prefix_name + choices[0] + ";"
-                else:
-                    print "      case K is"
-                    gen_choices(choices)
-                    print "            return True;"
-                    print "         when others =>"
-                    print "            return False;"
-                    print "      end case;"
-                print "   end Has_" + f.name + ";"
-                print
-        elif l[0:3] == "end":
-            print l
-            break
-        else:
-            print l
-
-
-actions = {
-    "disp-nodes": do_disp_nodes,
-    "disp-kinds": do_disp_kinds,
-    "disp-formats": do_disp_formats,
-    "disp-funcs": do_disp_funcs,
-    "disp-types": do_disp_types,
-    "get_format": do_get_format,
-    "body": do_body,
-    "meta_specs": do_meta_specs,
-    "meta_body": do_meta_body,
-}
-
-
-def main():
-    parser = argparse.ArgumentParser(description="Meta-grammar processor")
-    parser.add_argument("action", choices=actions.keys(), default="disp-nodes")
-    parser.add_argument(
-        "--field-file",
-        dest="field_file",
-        default="nodes.ads",
-        help="specify file which defines fields",
-    )
-    parser.add_argument(
-        "--kind-file",
-        dest="kind_file",
-        default="iirs.ads",
-        help="specify file which defines nodes kind",
-    )
-    parser.add_argument(
-        "--node-file",
-        dest="node_file",
-        default="iirs.ads",
-        help="specify file which defines nodes and methods",
-    )
-    parser.add_argument(
-        "--template-file",
-        dest="template_file",
-        default="iirs.adb.in",
-        help="specify template body file",
-    )
-    parser.add_argument(
-        "--meta-basename",
-        dest="meta_basename",
-        default="nodes_meta",
-        help="specify base name of meta files",
-    )
-    parser.add_argument(
-        "--kind-type", dest="kind_type", default="Iir_Kind", help="name of kind type"
-    )
-    parser.add_argument(
-        "--kind-prefix",
-        dest="kind_prefix",
-        default="Iir_Kind_",
-        help="prefix for kind literals",
-    )
-    parser.add_argument(
-        "--kind-range-prefix",
-        dest="kind_range_prefix",
-        default="Iir_Kinds_",
-        help="prefix for kind subtype (range)",
-    )
-    parser.add_argument(
-        "--node-type", dest="node_type", default="Iir", help="name of the node type"
-    )
-    parser.add_argument(
-        "--keep-order",
-        dest="flag_keep_order",
-        action="store_true",
-        help="keep field order of nodes",
-    )
-    parser.set_defaults(flag_keep_order=False)
-    args = parser.parse_args()
-
-    # At some point, it would be simpler to create a class...
-    global formats, fields, nodes, kinds, kinds_ranges, funcs
-
-    global type_name, prefix_name, template_file, node_type, meta_base_file
-    global prefix_range_name, flag_keep_order, kind_file
-
-    type_name = args.kind_type
-    prefix_name = args.kind_prefix
-    prefix_range_name = args.kind_range_prefix
-    template_file = args.template_file
-    node_type = args.node_type
-    meta_base_file = args.meta_basename
-    flag_keep_order = args.flag_keep_order
-
-    field_file = args.field_file
-    kind_file = args.kind_file
-    node_file = args.node_file
-
-    try:
-        (formats, fields) = read_fields(field_file)
-        (kinds, kinds_ranges) = read_kinds(kind_file)
-        funcs = read_methods(node_file)
-        nodes = read_nodes(node_file, kinds, kinds_ranges, fields, funcs)
-
-    except ParseError as e:
-        print >> sys.stderr, e
-        print >> sys.stderr, "in {0}:{1}:{2}".format(e.lr.filename, e.lr.lineno, e.lr.l)
-        sys.exit(1)
-
-    f = actions.get(args.action, None)
-    if not f:
-        print >> sys.stderr, "Action {0} is unknown".format(args.action)
-        sys.exit(1)
-    f()
-
-
-if __name__ == "__main__":
-    main()
diff --git a/pyGHDL/xtools/pnodespy.py b/pyGHDL/xtools/pnodespy.py
deleted file mode 100755
index 0e0f5ba9e..000000000
--- a/pyGHDL/xtools/pnodespy.py
+++ /dev/null
@@ -1,263 +0,0 @@
-#!/usr/bin/env python
-
-"""Like pnodes but output for python"""
-
-from __future__ import print_function
-import pnodes
-import re
-
-libname = "libghdl"
-
-
-def print_enum(name, vals):
-    print()
-    print()
-    print("class {0}:".format(name))
-    for n, k in enumerate(vals):
-        if k == "None":
-            k = "PNone"
-        print("    {0} = {1}".format(k, n))
-
-
-def do_class_kinds():
-    print_enum(pnodes.prefix_name.rstrip("_"), pnodes.kinds)
-    print()
-    print()
-    print("class Iir_Kinds:")
-    for k, v in pnodes.kinds_ranges.items():
-        print("    {0} = [".format(k))
-        for e in v:
-            print("        Iir_Kind.{},".format(e))
-        print("    ]")
-        print()
-
-
-def do_iirs_subprg():
-    classname = "vhdl__nodes"
-    print()
-    print("Get_Kind = {0}.{1}__get_kind".format(libname, classname))
-    print("Get_Location = {0}.{1}__get_location".format(libname, classname))
-    for k in pnodes.funcs:
-        print()
-        print(
-            "Get_{0} = {1}.{2}__get_{3}".format(
-                k.name, libname, classname, k.name.lower()
-            )
-        )
-        print()
-        print(
-            "Set_{0} = {1}.{2}__set_{3}".format(
-                k.name, libname, classname, k.name.lower(), k.pname, k.rname
-            )
-        )
-
-
-def do_libghdl_elocations():
-    classname = "vhdl__elocations"
-    print("from libghdl import libghdl")
-    print()
-    for k in pnodes.funcs:
-        print()
-        print(
-            "Get_{0} = {1}.{2}__get_{3}".format(
-                k.name, libname, classname, k.name.lower()
-            )
-        )
-        print()
-        print(
-            "Set_{0} = {1}.{2}__set_{3}".format(
-                k.name, libname, classname, k.name.lower(), k.pname, k.rname
-            )
-        )
-
-
-def do_class_types():
-    print_enum("types", pnodes.get_types())
-
-
-def do_types_subprg():
-    print()
-    for k in pnodes.get_types():
-        print()
-        print("Get_{0} = {1}.vhdl__nodes_meta__get_{2}".format(k, libname, k.lower()))
-
-
-def do_has_subprg():
-    print()
-    for f in pnodes.funcs:
-        print()
-        print("Has_{0} =\\".format(f.name))
-        print("    {0}.vhdl__nodes_meta__has_{1}".format(libname, f.name.lower()))
-
-
-def do_class_field_attributes():
-    print_enum("Attr", ["ANone" if a == "None" else a for a in pnodes.get_attributes()])
-
-
-def do_class_fields():
-    print_enum("fields", [f.name for f in pnodes.funcs])
-
-
-def read_enum(filename, type_name, prefix, class_name, g=lambda m: m.group(1)):
-    """Read an enumeration declaration from :param filename:"""
-    pat_decl = re.compile(r"   type {0} is$".format(type_name))
-    pat_enum = re.compile(r"      {0}(\w+),?( *-- .*)?$".format(prefix))
-    pat_comment = re.compile(r" *-- .*$")
-    lr = pnodes.linereader(filename)
-    while not pat_decl.match(lr.get()):
-        pass
-    line = lr.get()
-    if line != "     (\n":
-        raise pnodes.ParseError(
-            lr, "{}:{}: missing open parenthesis".format(filename, lr.lineno)
-        )
-    toks = []
-    while True:
-        line = lr.get()
-        if line == "     );\n":
-            break
-        m = pat_enum.match(line)
-        if m:
-            toks.append(g(m))
-        elif pat_comment.match(line):
-            pass
-        elif line == "\n":
-            pass
-        else:
-            print(line, file=sys.stderr)
-            raise pnodes.ParseError(
-                lr,
-                "{}:{}: incorrect line in enum {}".format(
-                    filename, lr.lineno, type_name
-                ),
-            )
-    print_enum(class_name, toks)
-
-
-def read_spec_enum(type_name, prefix, class_name):
-    """Read an enumeration declaration from iirs.ads"""
-    read_enum(pnodes.kind_file, type_name, prefix, class_name)
-
-
-def do_libghdl_nodes():
-    print("from libghdl import libghdl")
-    print(
-        """
-Null_Iir = 0
-
-Null_Iir_List = 0
-Iir_List_All = 1
-
-Null_Iir_Flist = 0
-Iir_Flist_Others = 1
-Iir_Flist_All = 2
-"""
-    )
-    do_class_kinds()
-    read_spec_enum("Iir_Mode", "Iir_", "Iir_Mode")
-    read_spec_enum("Iir_Staticness", "", "Iir_Staticness")
-    read_spec_enum("Iir_Constraint", "", "Iir_Constraint")
-    read_spec_enum("Iir_Delay_Mechanism", "Iir_", "Iir_Delay_Mechanism")
-    read_spec_enum("Date_State_Type", "Date_", "Date_State")
-    read_spec_enum("Iir_Predefined_Functions", "Iir_Predefined_", "Iir_Predefined")
-    do_iirs_subprg()
-
-
-def do_libghdl_meta():
-    print("from libghdl import libghdl")
-    print(
-        """
-
-# From nodes_meta
-get_fields_first = libghdl.vhdl__nodes_meta__get_fields_first
-
-get_fields_last = libghdl.vhdl__nodes_meta__get_fields_last
-
-get_field_by_index = libghdl.vhdl__nodes_meta__get_field_by_index
-
-get_field_type = libghdl.vhdl__nodes_meta__get_field_type
-
-get_field_attribute = libghdl.vhdl__nodes_meta__get_field_attribute"""
-    )
-    do_class_types()
-    do_class_field_attributes()
-    do_class_fields()
-    do_types_subprg()
-    do_has_subprg()
-
-
-def do_libghdl_names():
-    pat_name_first = re.compile(r"   Name_(\w+)\s+: constant Name_Id := (\d+);")
-    pat_name_def = re.compile(
-        r"   Name_(\w+)\s+:\s+constant Name_Id :=\s+Name_(\w+)( \+ (\d+))?;"
-    )
-    dict = {}
-    lr = pnodes.linereader("../std_names.ads")
-    while True:
-        line = lr.get()
-        m = pat_name_first.match(line)
-        if m:
-            name_def = m.group(1)
-            val = int(m.group(2))
-            dict[name_def] = val
-            res = [(name_def, val)]
-            break
-    val_max = 1
-    while True:
-        line = lr.get()
-        if line == "end Std_Names;\n":
-            break
-        if line.endswith(":=\n"):
-            line = line.rstrip() + lr.get()
-        m = pat_name_def.match(line)
-        if m:
-            name_def = m.group(1)
-            name_ref = m.group(2)
-            val = m.group(3)
-            if not val:
-                val = 0
-            val_ref = dict.get(name_ref, None)
-            if not val_ref:
-                raise pnodes.ParseError(lr, "name {0} not found".format(name_ref))
-            val = val_ref + int(val)
-            val_max = max(val_max, val)
-            dict[name_def] = val
-            res.append((name_def, val))
-    print("class Name:")
-    for n, v in res:
-        # Avoid clash with Python names
-        if n in ["False", "True", "None"]:
-            n = "N" + n
-        print("    {0} = {1}".format(n, v))
-
-
-def do_libghdl_tokens():
-    read_enum("vhdl-tokens.ads", "Token_Type", "Tok_", "Tok")
-
-
-def do_libghdl_errorout():
-    print("from libghdl import libghdl")
-    print("\n" "Enable_Warning = libghdl.errorout__enable_warning")
-    read_enum(
-        "../errorout.ads",
-        "Msgid_Type",
-        "(Msgid|Warnid)_",
-        "Msgid",
-        g=lambda m: m.group(1) + "_" + m.group(2),
-    )
-
-
-pnodes.actions.update(
-    {
-        "class-kinds": do_class_kinds,
-        "libghdl-nodes": do_libghdl_nodes,
-        "libghdl-meta": do_libghdl_meta,
-        "libghdl-names": do_libghdl_names,
-        "libghdl-tokens": do_libghdl_tokens,
-        "libghdl-elocs": do_libghdl_elocations,
-        "libghdl-errorout": do_libghdl_errorout,
-    }
-)
-
-
-pnodes.main()
diff --git a/scripts/pnodes.py b/scripts/pnodes.py
new file mode 100755
index 000000000..793c1c712
--- /dev/null
+++ b/scripts/pnodes.py
@@ -0,0 +1,988 @@
+#!/usr/bin/env python
+
+import re
+import sys
+import argparse
+
+field_file = "nodes.ads"
+kind_file = "iirs.ads"
+node_file = "iirs.ads"
+template_file = "iirs.adb.in"
+meta_base_file = "nodes_meta"
+prefix_name = "Iir_Kind_"
+prefix_range_name = "Iir_Kinds_"
+type_name = "Iir_Kind"
+node_type = "Iir"
+conversions = ["uc", "pos", "grp"]
+
+
+class FuncDesc:
+    def __init__(self, name, fields, conv, acc, pname, ptype, rname, rtype):
+        self.name = name
+        self.fields = fields  # List of physical fields used
+        self.conv = conv
+        self.acc = acc  # access: Chain, Chain_Next, Ref, Of_Ref, Maybe_Ref,
+        #                 Forward_Ref, Maybe_Forward_Ref
+        self.pname = pname  # Parameter mame
+        self.ptype = ptype  # Parameter type
+        self.rname = rname  # value name (for procedure)
+        self.rtype = rtype  # value type
+
+
+class NodeDesc:
+    def __init__(self, name, format, fields, attrs):
+        self.name = name
+        self.format = format
+        self.fields = fields  # {field: FuncDesc} dict, defined for all fields
+        self.attrs = attrs  # A {attr: FuncDesc} dict
+        self.order = []  # List of fields name, in order of appearance.
+
+
+class line:
+    def __init__(self, string, no):
+        self.l = string
+        self.n = no
+
+
+class EndOfFile(Exception):
+    def __init__(self, filename):
+        self.filename = filename
+
+    def __str__(self):
+        return "end of file " + self.filename
+
+
+class linereader:
+    def __init__(self, filename):
+        self.filename = filename
+        self.f = open(filename)
+        self.lineno = 0
+        self.l = ""
+
+    def get(self):
+        self.l = self.f.readline()
+        if not self.l:
+            raise EndOfFile(self.filename)
+        self.lineno = self.lineno + 1
+        return self.l
+
+
+class ParseError(Exception):
+    def __init__(self, lr, msg):
+        self.lr = lr
+        self.msg = msg
+
+    def __str__(self):
+        return "Error: " + self.msg
+        return (
+            "Parse error at " + self.lr.filname + ":" + self.lr.lineno + ": " + self.msg
+        )
+
+
+# Return fields description.
+# This is a dictionary.  The keys represent the possible format of a node.
+# The values are dictionnaries representing fields.  Keys are fields name, and
+# values are fields type.
+def read_fields(file):
+    fields = {}
+    formats = []
+    lr = linereader(file)
+
+    #  Search for 'type Format_Type is'
+    while lr.get() != "   type Format_Type is\n":
+        pass
+
+    # Skip '('
+    if lr.get() != "     (\n":
+        raise "no open parenthesis after Format_Type"
+
+    # Read formats
+    l = lr.get()
+    pat_field_name = re.compile("      Format_(\w+),?\n")
+    while l != "     );\n":
+        m = pat_field_name.match(l)
+        if m is None:
+            print l
+            raise "bad literal within Format_Type"
+        name = m.group(1)
+        formats.append(name)
+        fields[name] = {}
+        l = lr.get()
+
+    # Read fields
+    l = lr.get()
+    pat_fields = re.compile("   -- Fields of Format_(\w+):\n")
+    pat_field_desc = re.compile("   --   (\w+) : (\w+).*\n")
+    format_name = ""
+    common_desc = {}
+
+    # Read until common fields.
+    while l != "   -- Common fields are:\n":
+        l = lr.get()
+    format_name = "Common"
+    nbr_formats = 0
+
+    while True:
+        # 1) Read field description
+        l = lr.get()
+        desc = common_desc.copy()
+        while True:
+            m = pat_field_desc.match(l)
+            if m is None:
+                break
+            desc[m.group(1)] = m.group(2)
+            l = lr.get()
+            # print 'For: ' + format_name + ': ' + m.group(1)
+
+        # 2) Disp
+        if format_name == "Common":
+            common_desc = desc
+        else:
+            fields[format_name] = desc
+
+        # 3) Read next format
+        if l == "\n":
+            if nbr_formats == len(fields):
+                break
+            else:
+                l = lr.get()
+
+        # One for a format
+        m = pat_fields.match(l)
+        if m is not None:
+            format_name = m.group(1)
+            if format_name not in fields:
+                raise ParseError(lr, "Format " + format_name + " is unknown")
+            nbr_formats = nbr_formats + 1
+        else:
+            raise ParseError(lr, "unhandled format line")
+
+    return (formats, fields)
+
+
+# Read kinds and kinds ranges.
+def read_kinds(filename):
+    lr = linereader(filename)
+    kinds = []
+    #  Search for 'type Iir_Kind is'
+    while lr.get() != "   type " + type_name + " is\n":
+        pass
+    # Skip '('
+    if lr.get() != "     (\n":
+        raise ParseError(lr, 'no open parenthesis after "type ' + type_name + '"')
+
+    # Read literals
+    pat_node = re.compile("      " + prefix_name + "(\w+),?( +-- .*)?\n")
+    pat_comment = re.compile("( +-- .*)?\n")
+    while True:
+        l = lr.get()
+        if l == "     );\n":
+            break
+        m = pat_node.match(l)
+        if m:
+            kinds.append(m.group(1))
+            continue
+        m = pat_comment.match(l)
+        if not m:
+            raise ParseError(lr, "Unknown line within kind declaration")
+
+    # Check subtypes
+    pat_subtype = re.compile("   subtype " + r"(\w+) is " + type_name + " range\n")
+    pat_first = re.compile("     " + prefix_name + r"(\w+) ..\n")
+    pat_last = re.compile("     " + prefix_name + r"(\w+);\n")
+    pat_middle = re.compile("   --" + prefix_name + r"(\w+)\n")
+    kinds_ranges = {}
+    while True:
+        l = lr.get()
+        # Start of methods is also end of subtypes.
+        if l == "   -- General methods.\n":
+            break
+        # Found a subtype.
+        m = pat_subtype.match(l)
+        if m:
+            # Check first bound
+            name = m.group(1)
+            if not name.startswith(prefix_range_name):
+                raise ParseError(lr, "incorrect prefix for subtype")
+            name = name[len(prefix_range_name) :]
+            l = lr.get()
+            mf = pat_first.match(l)
+            if not mf:
+                raise ParseError(lr, "badly formated first bound of subtype")
+            first = kinds.index(mf.group(1))
+            idx = first
+            has_middle = None
+            # Read until last bound
+            while True:
+                l = lr.get()
+                ml = pat_middle.match(l)
+                if ml:
+                    # Check element in the middle
+                    n = ml.group(1)
+                    if n not in kinds:
+                        raise ParseError(lr, "unknown kind " + n + " in subtype")
+                    if kinds.index(n) != idx + 1:
+                        raise ParseError(
+                            lr, "missing " + kinds[idx + 1] + " in subtype"
+                        )
+                    has_middle = True
+                    idx = idx + 1
+                else:
+                    # Check last bound
+                    ml = pat_last.match(l)
+                    if ml:
+                        last = kinds.index(ml.group(1))
+                        if last != idx + 1 and has_middle:
+                            raise ParseError(
+                                lr, "missing " + kinds[idx] + " in subtype"
+                            )
+                        break
+                    raise ParseError(lr, "unhandled line in subtype")
+            kinds_ranges[name] = kinds[first : last + 1]
+    return (kinds, kinds_ranges)
+
+
+# Read functions
+def read_methods(filename):
+    lr = linereader(filename)
+    funcs = []
+    pat_field = re.compile(r"   --  Field: ([\w,]+)( \w+)?( \(\w+\))?\n")
+    pat_conv = re.compile(r"^ \((\w+)\)$")
+    pat_func = re.compile(r"   function Get_(\w+) \((\w+) : (\w+)\) return (\w+);\n")
+    pat_proc = re.compile(r"   procedure Set_(\w+) \((\w+) : (\w+); (\w+) : (\w+)\);\n")
+    pat_end = re.compile("end [A-Za-z.]+;\n")
+    while True:
+        l = lr.get()
+        # Start of methods
+        if l == "   -- General methods.\n":
+            break
+    while True:
+        l = lr.get()
+        if pat_end.match(l):
+            break
+        m = pat_field.match(l)
+        if m:
+            fields = m.group(1).split(",")
+            # Extract access modifier
+            acc = m.group(2)
+            if acc:
+                acc = acc.strip()
+            # Extract conversion
+            conv = m.group(3)
+            if conv:
+                mc = pat_conv.match(conv)
+                if not mc:
+                    raise ParseError(lr, "conversion ill formed")
+                conv = mc.group(1)
+                if conv not in conversions:
+                    raise ParseError(lr, "unknown conversion " + conv)
+            else:
+                conv = None
+            if len(fields) > 1 and conv != "grp":
+                raise ParseError(lr, "bad conversion for multiple fields")
+            # Read function
+            l = lr.get()
+            mf = pat_func.match(l)
+            if not mf:
+                raise ParseError(lr, "function declaration expected after Field")
+            # Read procedure
+            l = lr.get()
+            mp = pat_proc.match(l)
+            if not mp:
+                raise ParseError(lr, "procedure declaration expected after function")
+            # Consistency check between function and procedure
+            if mf.group(1) != mp.group(1):
+                raise ParseError(lr, "function and procedure name mismatch")
+            if mf.group(2) != mp.group(2):
+                raise ParseError(lr, "parameter name mismatch with function")
+            if mf.group(3) != mp.group(3):
+                raise ParseError(lr, "parameter type mismatch with function")
+            if mf.group(4) != mp.group(5):
+                raise ParseError(lr, "result type mismatch with function")
+            funcs.append(
+                FuncDesc(
+                    mf.group(1),
+                    fields,
+                    conv,
+                    acc,
+                    mp.group(2),
+                    mp.group(3),
+                    mp.group(4),
+                    mp.group(5),
+                )
+            )
+
+    return funcs
+
+
+# Read description for one node
+# LR is the line reader.  NAMES is the list of (node name, format)
+#  (one description may describe several nodes).
+# A comment start at column 2 or 4 or later.
+def read_nodes_fields(lr, names, fields, nodes, funcs_dict):
+    pat_only = re.compile("   -- Only for " + prefix_name + "(\w+):\n")
+    pat_only_bad = re.compile("   -- *Only for.*\n")
+    pat_field = re.compile("   --   Get/Set_(\w+) \((Alias )?([\w,]+)\)\n")
+    pat_comment = re.compile("   --(|  [^ ].*|    .*)\n")
+
+    # Create nodes
+    cur_nodes = []
+    for (nm, fmt) in names:
+        if fmt not in fields:
+            raise ParseError(lr, 'unknown format "{}"'.format(fmt))
+        n = NodeDesc(nm, fmt, {x: None for x in fields[fmt]}, {})
+        nodes[nm] = n
+        cur_nodes.append(n)
+
+    # Skip comments
+    l = lr.l
+    while pat_comment.match(l):
+        l = lr.get()
+
+    # Look for fields
+    while l != "\n":
+        # Skip comments
+        while pat_comment.match(l):
+            l = lr.get()
+
+        # Handle 'Only ...'
+        m = pat_only.match(l)
+        if m:
+            only_nodes = []
+            while True:
+                name = m.group(1)
+                n = nodes.get(name, None)
+                if n is None:
+                    raise ParseError(lr, "node is unknown")
+                if n not in cur_nodes:
+                    raise ParseError(lr, "node not currently described")
+                only_nodes.append(n)
+                l = lr.get()
+                m = pat_only.match(l)
+                if not m:
+                    break
+        else:
+            # By default a field applies to all nodes.
+            only_nodes = cur_nodes
+
+        # Skip comments
+        while pat_comment.match(l):
+            l = lr.get()
+
+        # Handle field: '--  Get/Set_FUNC (Alias? FIELD)'
+        m = pat_field.match(l)
+        if not m:
+            if pat_only_bad.match(l):
+                raise ParseError(lr, "misleading 'Only for' comment")
+            else:
+                raise ParseError(lr, "bad line in node description")
+
+        func = m.group(1)
+        alias = m.group(2)
+        fields = m.group(3).split(",")
+
+        # Check the function exists and if the field is correct.
+        if func not in funcs_dict:
+            raise ParseError(lr, "unknown function")
+        func = funcs_dict[func]
+        if func.fields != fields:
+            raise ParseError(lr, "fields mismatch")
+
+        for c in only_nodes:
+            for f in fields:
+                if f not in c.fields:
+                    raise ParseError(lr, "field " + f + " does not exist in node")
+            if not alias:
+                for f in fields:
+                    if c.fields[f]:
+                        raise ParseError(lr, "field " + f + " already used")
+                    c.fields[f] = func
+                    c.order.append(f)
+            c.attrs[func.name] = func
+
+        l = lr.get()
+
+
+def read_nodes(filename, kinds, kinds_ranges, fields, funcs):
+    """Read description for all nodes."""
+    lr = linereader(filename)
+    funcs_dict = {x.name: x for x in funcs}
+    nodes = {}
+
+    # Skip until start
+    while lr.get() != "   -- Start of " + type_name + ".\n":
+        pass
+
+    pat_decl = re.compile("   -- " + prefix_name + "(\w+) \((\w+)\)\n")
+    pat_decls = re.compile("   -- " + prefix_range_name + "(\w+) \((\w+)\)\n")
+    pat_comment_line = re.compile("   --+\n")
+    pat_comment_box = re.compile("   --(  .*)?\n")
+    while True:
+        l = lr.get()
+        if l == "   -- End of " + type_name + ".\n":
+            break
+        if l == "\n":
+            continue
+        m = pat_decl.match(l)
+        if m:
+            # List of nodes being described by the current description.
+            names = []
+
+            # Declaration of the first node
+            while True:
+                name = m.group(1)
+                if name not in kinds:
+                    raise ParseError(lr, "unknown node")
+                fmt = m.group(2)
+                names.append((name, fmt))
+                if name in nodes:
+                    raise ParseError(lr, "node {} already described".format(name))
+                # There might be several nodes described at once.
+                l = lr.get()
+                m = pat_decl.match(l)
+                if not m:
+                    break
+            read_nodes_fields(lr, names, fields, nodes, funcs_dict)
+            continue
+        m = pat_decls.match(l)
+        if m:
+            # List of nodes being described by the current description.
+            name = m.group(1)
+            fmt = m.group(2)
+            names = [(k, fmt) for k in kinds_ranges[name]]
+            l = lr.get()
+            read_nodes_fields(lr, names, fields, nodes, funcs_dict)
+            continue
+        if pat_comment_line.match(l) or pat_comment_box.match(l):
+            continue
+        raise ParseError(lr, "bad line in node description")
+
+    for k in kinds:
+        if k not in nodes:
+            raise ParseError(lr, 'no description for "{}"'.format(k))
+    return nodes
+
+
+def gen_choices(choices):
+    """Generate a choice 'when A | B ... Z =>' using elements of CHOICES."""
+    is_first = True
+    for c in choices:
+        if is_first:
+            print "        ",
+            print "when",
+        else:
+            print
+            print "        ",
+            print "  |",
+        print prefix_name + c,
+        is_first = False
+    print "=>"
+
+
+def gen_get_format(formats, nodes, kinds):
+    """Generate the Get_Format function."""
+    print "   function Get_Format (Kind : " + type_name + ") " + "return Format_Type is"
+    print "   begin"
+    print "      case Kind is"
+    for f in formats:
+        choices = [k for k in kinds if nodes[k].format == f]
+        gen_choices(choices)
+        print "            return Format_" + f + ";"
+    print "      end case;"
+    print "   end Get_Format;"
+
+
+def gen_subprg_header(decl):
+    if len(decl) < 76:
+        print decl + " is"
+    else:
+        print decl
+        print "   is"
+    print "   begin"
+
+
+def gen_assert(func):
+    print "      pragma Assert (" + func.pname + " /= Null_" + node_type + ");"
+    cond = "(Has_" + func.name + " (Get_Kind (" + func.pname + ")),"
+    msg = '"no field ' + func.name + '");'
+    if len(cond) < 60:
+        print "      pragma Assert " + cond
+        print "                     " + msg
+    else:
+        print "      pragma Assert"
+        print "         " + cond
+        print "          " + msg
+
+
+def get_field_type(fields, f):
+    for fld in fields.values():
+        if f in fld:
+            return fld[f]
+    return None
+
+
+def gen_get_set(func, nodes, fields):
+    """Generate Get_XXX/Set_XXX subprograms for FUNC."""
+    rtype = func.rtype
+    # If the function needs several fields, it must be user defined
+    if func.conv == "grp":
+        print "   type %s_Conv is record" % rtype
+        for f in func.fields:
+            print "      %s: %s;" % (f, get_field_type(fields, f))
+        print "   end record;"
+        print "   pragma Pack (%s_Conv);" % rtype
+        print "   pragma Assert (%s_Conv'Size = %s'Size);" % (rtype, rtype)
+        print
+    else:
+        f = func.fields[0]
+        g = "Get_" + f + " (" + func.pname + ")"
+
+    s = func.rname
+    if func.conv:
+        if func.conv == "uc":
+            field_type = get_field_type(fields, f)
+            g = field_type + "_To_" + rtype + " (" + g + ")"
+            s = rtype + "_To_" + field_type + " (" + s + ")"
+        elif func.conv == "pos":
+            g = rtype + "'Val (" + g + ")"
+            s = rtype + "'Pos (" + s + ")"
+
+    subprg = (
+        "   function Get_"
+        + func.name
+        + " ("
+        + func.pname
+        + " : "
+        + func.ptype
+        + ") return "
+        + rtype
+    )
+    if func.conv == "grp":
+        print subprg
+        print "   is"
+        print "      function To_%s is new Ada.Unchecked_Conversion" % func.rtype
+        print "         (%s_Conv, %s);" % (rtype, rtype)
+        print "      Conv : %s_Conv;" % rtype
+        print "   begin"
+    else:
+        gen_subprg_header(subprg)
+    gen_assert(func)
+    if func.conv == "grp":
+        for f in func.fields:
+            print "      Conv.%s := Get_%s (%s);" % (f, f, func.pname)
+        g = "To_%s (Conv)" % rtype
+    print "      return " + g + ";"
+    print "   end Get_" + func.name + ";"
+    print
+
+    subprg = (
+        "   procedure Set_"
+        + func.name
+        + " ("
+        + func.pname
+        + " : "
+        + func.ptype
+        + "; "
+        + func.rname
+        + " : "
+        + func.rtype
+        + ")"
+    )
+    if func.conv == "grp":
+        print subprg
+        print "   is"
+        print "      function To_%s_Conv is new Ada.Unchecked_Conversion" % func.rtype
+        print "         (%s, %s_Conv);" % (rtype, rtype)
+        print "      Conv : %s_Conv;" % rtype
+        print "   begin"
+    else:
+        gen_subprg_header(subprg)
+    gen_assert(func)
+    if func.conv == "grp":
+        print "      Conv := To_%s_Conv (%s);" % (rtype, func.rname)
+        for f in func.fields:
+            print "      Set_%s (%s, Conv.%s);" % (f, func.pname, f)
+    else:
+        print "      Set_" + f + " (" + func.pname + ", " + s + ");"
+    print "   end Set_" + func.name + ";"
+    print
+
+
+def funcs_of_node(n):
+    return sorted([fv.name for fv in n.fields.values() if fv])
+
+
+def gen_has_func_spec(name, suff):
+    spec = "   function Has_" + name + " (K : " + type_name + ")"
+    ret = " return Boolean" + suff
+    if len(spec) < 60:
+        print spec + ret
+    else:
+        print spec
+        print "     " + ret
+
+
+def do_disp_formats():
+    for fmt in fields:
+        print "Fields of Format_" + fmt
+        fld = fields[fmt]
+        for k in fld:
+            print "  " + k + " (" + fld[k] + ")"
+
+
+def do_disp_kinds():
+    print "Kinds are:"
+    for k in kinds:
+        print "  " + prefix_name + k
+
+
+def do_disp_funcs():
+    print "Functions are:"
+    for f in funcs:
+        s = "{0} ({1}: {2}".format(f.name, f.field, f.rtype)
+        if f.acc:
+            s += " acc:" + f.acc
+        if f.conv:
+            s += " conv:" + f.conv
+        s += ")"
+        print s
+
+
+def do_disp_types():
+    print "Types are:"
+    s = set([])
+    for f in funcs:
+        s |= set([f.rtype])
+    for t in sorted(s):
+        print "  " + t
+
+
+def do_disp_nodes():
+    for k in kinds:
+        v = nodes[k]
+        print prefix_name + k + " (" + v.format + ")"
+        flds = [fk for fk, fv in v.fields.items() if fv]
+        for fk in sorted(flds):
+            print "  " + fk + ": " + v.fields[fk].name
+
+
+def do_get_format():
+    gen_get_format(formats, nodes)
+
+
+def do_body():
+    lr = linereader(template_file)
+    while True:
+        l = lr.get().rstrip()
+        print l
+        if l == "   --  Subprograms":
+            gen_get_format(formats, nodes, kinds)
+            print
+            for f in funcs:
+                gen_get_set(f, nodes, fields)
+        if l[0:3] == "end":
+            break
+
+
+def get_types():
+    s = set([])
+    for f in funcs:
+        s |= set([f.rtype])
+    return [t for t in sorted(s)]
+
+
+def get_attributes():
+    s = set([])
+    for f in funcs:
+        if f.acc:
+            s |= set([f.acc])
+    res = [t for t in sorted(s)]
+    res.insert(0, "None")
+    return res
+
+
+def gen_enum(prefix, vals):
+    last = None
+    for v in vals:
+        if last:
+            print last + ","
+        last = prefix + v
+    print last
+
+
+def do_meta_specs():
+    lr = linereader(meta_base_file + ".ads.in")
+    types = get_types()
+    while True:
+        l = lr.get().rstrip()
+        if l == "      --  TYPES":
+            gen_enum("      Type_", types)
+        elif l == "      --  FIELDS":
+            gen_enum("      Field_", [f.name for f in funcs])
+        elif l == "      --  ATTRS":
+            gen_enum("      Attr_", get_attributes())
+        elif l == "   --  FUNCS":
+            for t in types:
+                print "   function Get_" + t
+                print "      (N : " + node_type + "; F : Fields_Enum) return " + t + ";"
+                print "   procedure Set_" + t
+                print "      (N : " + node_type + "; F : Fields_Enum; V: " + t + ");"
+                print
+            for f in funcs:
+                gen_has_func_spec(f.name, ";")
+        elif l[0:3] == "end":
+            print l
+            break
+        else:
+            print l
+
+
+def do_meta_body():
+    lr = linereader(meta_base_file + ".adb.in")
+    while True:
+        l = lr.get().rstrip()
+        if l == "      --  FIELDS_TYPE":
+            last = None
+            for f in funcs:
+                if last:
+                    print last + ","
+                last = "      Field_" + f.name + " => Type_" + f.rtype
+            print last
+        elif l == "         --  FIELD_IMAGE":
+            for f in funcs:
+                print "         when Field_" + f.name + " =>"
+                print '            return "' + f.name.lower() + '";'
+        elif l == "         --  IIR_IMAGE":
+            for k in kinds:
+                print "         when " + prefix_name + k + " =>"
+                print '            return "' + k.lower() + '";'
+        elif l == "         --  FIELD_ATTRIBUTE":
+            for f in funcs:
+                print "         when Field_" + f.name + " =>"
+                if f.acc:
+                    attr = f.acc
+                else:
+                    attr = "None"
+                print "            return Attr_" + attr + ";"
+        elif l == "      --  FIELDS_ARRAY":
+            last = None
+            nodes_types = [node_type, node_type + "_List", node_type + "_Flist"]
+            for k in kinds:
+                v = nodes[k]
+                if last:
+                    print last + ","
+                last = None
+                print "      --  " + prefix_name + k
+                # Get list of physical fields for V, in some order.
+                if flag_keep_order:
+                    flds = v.order
+                else:
+                    # First non Iir and no Iir_List.
+                    flds = sorted(
+                        [
+                            fk
+                            for fk, fv in v.fields.items()
+                            if fv and fv.rtype not in nodes_types
+                        ]
+                    )
+                    # Then Iir and Iir_List in order of appearance
+                    flds += (fv for fv in v.order if v.fields[fv].rtype in nodes_types)
+                # Print the corresponding node field, but remove duplicate due
+                # to 'grp'.
+                fldsn = []
+                for fk in flds:
+                    if last:
+                        print last + ","
+                    # Remove duplicate
+                    fn = v.fields[fk].name
+                    if fn not in fldsn:
+                        last = "      Field_" + fn
+                        fldsn.append(fn)
+                    else:
+                        last = None
+            if last:
+                print last
+        elif l == "      --  FIELDS_ARRAY_POS":
+            pos = -1
+            last = None
+            for k in kinds:
+                v = nodes[k]
+                # Create a set to remove duplicate for 'grp'.
+                flds = set([fv.name for fk, fv in v.fields.items() if fv])
+                pos += len(flds)
+                if last:
+                    print last + ","
+                last = "      " + prefix_name + k + " => {}".format(pos)
+            print last
+        elif l == "   --  FUNCS_BODY":
+            # Build list of types
+            s = set([])
+            for f in funcs:
+                s |= set([f.rtype])
+            types = [t for t in sorted(s)]
+            for t in types:
+                print "   function Get_" + t
+                print "      (N : " + node_type + "; F : Fields_Enum) return " + t + " is"
+                print "   begin"
+                print "      pragma Assert (Fields_Type (F) = Type_" + t + ");"
+                print "      case F is"
+                for f in funcs:
+                    if f.rtype == t:
+                        print "         when Field_" + f.name + " =>"
+                        print "            return Get_" + f.name + " (N);"
+                print "         when others =>"
+                print "            raise Internal_Error;"
+                print "      end case;"
+                print "   end Get_" + t + ";"
+                print
+                print "   procedure Set_" + t
+                print "      (N : " + node_type + "; F : Fields_Enum; V: " + t + ") is"
+                print "   begin"
+                print "      pragma Assert (Fields_Type (F) = Type_" + t + ");"
+                print "      case F is"
+                for f in funcs:
+                    if f.rtype == t:
+                        print "         when Field_" + f.name + " =>"
+                        print "            Set_" + f.name + " (N, V);"
+                print "         when others =>"
+                print "            raise Internal_Error;"
+                print "      end case;"
+                print "   end Set_" + t + ";"
+                print
+            for f in funcs:
+                gen_has_func_spec(f.name, " is")
+                choices = [k for k in kinds if f.name in nodes[k].attrs]
+                if len(choices) == 0:
+                    print "      pragma Unreferenced (K);"
+                print "   begin"
+                if len(choices) == 0:
+                    print "      return False;"
+                elif len(choices) == 1:
+                    print "      return K = " + prefix_name + choices[0] + ";"
+                else:
+                    print "      case K is"
+                    gen_choices(choices)
+                    print "            return True;"
+                    print "         when others =>"
+                    print "            return False;"
+                    print "      end case;"
+                print "   end Has_" + f.name + ";"
+                print
+        elif l[0:3] == "end":
+            print l
+            break
+        else:
+            print l
+
+
+actions = {
+    "disp-nodes": do_disp_nodes,
+    "disp-kinds": do_disp_kinds,
+    "disp-formats": do_disp_formats,
+    "disp-funcs": do_disp_funcs,
+    "disp-types": do_disp_types,
+    "get_format": do_get_format,
+    "body": do_body,
+    "meta_specs": do_meta_specs,
+    "meta_body": do_meta_body,
+}
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Meta-grammar processor")
+    parser.add_argument("action", choices=actions.keys(), default="disp-nodes")
+    parser.add_argument(
+        "--field-file",
+        dest="field_file",
+        default="nodes.ads",
+        help="specify file which defines fields",
+    )
+    parser.add_argument(
+        "--kind-file",
+        dest="kind_file",
+        default="iirs.ads",
+        help="specify file which defines nodes kind",
+    )
+    parser.add_argument(
+        "--node-file",
+        dest="node_file",
+        default="iirs.ads",
+        help="specify file which defines nodes and methods",
+    )
+    parser.add_argument(
+        "--template-file",
+        dest="template_file",
+        default="iirs.adb.in",
+        help="specify template body file",
+    )
+    parser.add_argument(
+        "--meta-basename",
+        dest="meta_basename",
+        default="nodes_meta",
+        help="specify base name of meta files",
+    )
+    parser.add_argument(
+        "--kind-type", dest="kind_type", default="Iir_Kind", help="name of kind type"
+    )
+    parser.add_argument(
+        "--kind-prefix",
+        dest="kind_prefix",
+        default="Iir_Kind_",
+        help="prefix for kind literals",
+    )
+    parser.add_argument(
+        "--kind-range-prefix",
+        dest="kind_range_prefix",
+        default="Iir_Kinds_",
+        help="prefix for kind subtype (range)",
+    )
+    parser.add_argument(
+        "--node-type", dest="node_type", default="Iir", help="name of the node type"
+    )
+    parser.add_argument(
+        "--keep-order",
+        dest="flag_keep_order",
+        action="store_true",
+        help="keep field order of nodes",
+    )
+    parser.set_defaults(flag_keep_order=False)
+    args = parser.parse_args()
+
+    # At some point, it would be simpler to create a class...
+    global formats, fields, nodes, kinds, kinds_ranges, funcs
+
+    global type_name, prefix_name, template_file, node_type, meta_base_file
+    global prefix_range_name, flag_keep_order, kind_file
+
+    type_name = args.kind_type
+    prefix_name = args.kind_prefix
+    prefix_range_name = args.kind_range_prefix
+    template_file = args.template_file
+    node_type = args.node_type
+    meta_base_file = args.meta_basename
+    flag_keep_order = args.flag_keep_order
+
+    field_file = args.field_file
+    kind_file = args.kind_file
+    node_file = args.node_file
+
+    try:
+        (formats, fields) = read_fields(field_file)
+        (kinds, kinds_ranges) = read_kinds(kind_file)
+        funcs = read_methods(node_file)
+        nodes = read_nodes(node_file, kinds, kinds_ranges, fields, funcs)
+
+    except ParseError as e:
+        print >> sys.stderr, e
+        print >> sys.stderr, "in {0}:{1}:{2}".format(e.lr.filename, e.lr.lineno, e.lr.l)
+        sys.exit(1)
+
+    f = actions.get(args.action, None)
+    if not f:
+        print >> sys.stderr, "Action {0} is unknown".format(args.action)
+        sys.exit(1)
+    f()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/pnodespy.py b/scripts/pnodespy.py
new file mode 100755
index 000000000..0e0f5ba9e
--- /dev/null
+++ b/scripts/pnodespy.py
@@ -0,0 +1,263 @@
+#!/usr/bin/env python
+
+"""Like pnodes but output for python"""
+
+from __future__ import print_function
+import pnodes
+import re
+
+libname = "libghdl"
+
+
+def print_enum(name, vals):
+    print()
+    print()
+    print("class {0}:".format(name))
+    for n, k in enumerate(vals):
+        if k == "None":
+            k = "PNone"
+        print("    {0} = {1}".format(k, n))
+
+
+def do_class_kinds():
+    print_enum(pnodes.prefix_name.rstrip("_"), pnodes.kinds)
+    print()
+    print()
+    print("class Iir_Kinds:")
+    for k, v in pnodes.kinds_ranges.items():
+        print("    {0} = [".format(k))
+        for e in v:
+            print("        Iir_Kind.{},".format(e))
+        print("    ]")
+        print()
+
+
+def do_iirs_subprg():
+    classname = "vhdl__nodes"
+    print()
+    print("Get_Kind = {0}.{1}__get_kind".format(libname, classname))
+    print("Get_Location = {0}.{1}__get_location".format(libname, classname))
+    for k in pnodes.funcs:
+        print()
+        print(
+            "Get_{0} = {1}.{2}__get_{3}".format(
+                k.name, libname, classname, k.name.lower()
+            )
+        )
+        print()
+        print(
+            "Set_{0} = {1}.{2}__set_{3}".format(
+                k.name, libname, classname, k.name.lower(), k.pname, k.rname
+            )
+        )
+
+
+def do_libghdl_elocations():
+    classname = "vhdl__elocations"
+    print("from libghdl import libghdl")
+    print()
+    for k in pnodes.funcs:
+        print()
+        print(
+            "Get_{0} = {1}.{2}__get_{3}".format(
+                k.name, libname, classname, k.name.lower()
+            )
+        )
+        print()
+        print(
+            "Set_{0} = {1}.{2}__set_{3}".format(
+                k.name, libname, classname, k.name.lower(), k.pname, k.rname
+            )
+        )
+
+
+def do_class_types():
+    print_enum("types", pnodes.get_types())
+
+
+def do_types_subprg():
+    print()
+    for k in pnodes.get_types():
+        print()
+        print("Get_{0} = {1}.vhdl__nodes_meta__get_{2}".format(k, libname, k.lower()))
+
+
+def do_has_subprg():
+    print()
+    for f in pnodes.funcs:
+        print()
+        print("Has_{0} =\\".format(f.name))
+        print("    {0}.vhdl__nodes_meta__has_{1}".format(libname, f.name.lower()))
+
+
+def do_class_field_attributes():
+    print_enum("Attr", ["ANone" if a == "None" else a for a in pnodes.get_attributes()])
+
+
+def do_class_fields():
+    print_enum("fields", [f.name for f in pnodes.funcs])
+
+
+def read_enum(filename, type_name, prefix, class_name, g=lambda m: m.group(1)):
+    """Read an enumeration declaration from :param filename:"""
+    pat_decl = re.compile(r"   type {0} is$".format(type_name))
+    pat_enum = re.compile(r"      {0}(\w+),?( *-- .*)?$".format(prefix))
+    pat_comment = re.compile(r" *-- .*$")
+    lr = pnodes.linereader(filename)
+    while not pat_decl.match(lr.get()):
+        pass
+    line = lr.get()
+    if line != "     (\n":
+        raise pnodes.ParseError(
+            lr, "{}:{}: missing open parenthesis".format(filename, lr.lineno)
+        )
+    toks = []
+    while True:
+        line = lr.get()
+        if line == "     );\n":
+            break
+        m = pat_enum.match(line)
+        if m:
+            toks.append(g(m))
+        elif pat_comment.match(line):
+            pass
+        elif line == "\n":
+            pass
+        else:
+            print(line, file=sys.stderr)
+            raise pnodes.ParseError(
+                lr,
+                "{}:{}: incorrect line in enum {}".format(
+                    filename, lr.lineno, type_name
+                ),
+            )
+    print_enum(class_name, toks)
+
+
+def read_spec_enum(type_name, prefix, class_name):
+    """Read an enumeration declaration from iirs.ads"""
+    read_enum(pnodes.kind_file, type_name, prefix, class_name)
+
+
+def do_libghdl_nodes():
+    print("from libghdl import libghdl")
+    print(
+        """
+Null_Iir = 0
+
+Null_Iir_List = 0
+Iir_List_All = 1
+
+Null_Iir_Flist = 0
+Iir_Flist_Others = 1
+Iir_Flist_All = 2
+"""
+    )
+    do_class_kinds()
+    read_spec_enum("Iir_Mode", "Iir_", "Iir_Mode")
+    read_spec_enum("Iir_Staticness", "", "Iir_Staticness")
+    read_spec_enum("Iir_Constraint", "", "Iir_Constraint")
+    read_spec_enum("Iir_Delay_Mechanism", "Iir_", "Iir_Delay_Mechanism")
+    read_spec_enum("Date_State_Type", "Date_", "Date_State")
+    read_spec_enum("Iir_Predefined_Functions", "Iir_Predefined_", "Iir_Predefined")
+    do_iirs_subprg()
+
+
+def do_libghdl_meta():
+    print("from libghdl import libghdl")
+    print(
+        """
+
+# From nodes_meta
+get_fields_first = libghdl.vhdl__nodes_meta__get_fields_first
+
+get_fields_last = libghdl.vhdl__nodes_meta__get_fields_last
+
+get_field_by_index = libghdl.vhdl__nodes_meta__get_field_by_index
+
+get_field_type = libghdl.vhdl__nodes_meta__get_field_type
+
+get_field_attribute = libghdl.vhdl__nodes_meta__get_field_attribute"""
+    )
+    do_class_types()
+    do_class_field_attributes()
+    do_class_fields()
+    do_types_subprg()
+    do_has_subprg()
+
+
+def do_libghdl_names():
+    pat_name_first = re.compile(r"   Name_(\w+)\s+: constant Name_Id := (\d+);")
+    pat_name_def = re.compile(
+        r"   Name_(\w+)\s+:\s+constant Name_Id :=\s+Name_(\w+)( \+ (\d+))?;"
+    )
+    dict = {}
+    lr = pnodes.linereader("../std_names.ads")
+    while True:
+        line = lr.get()
+        m = pat_name_first.match(line)
+        if m:
+            name_def = m.group(1)
+            val = int(m.group(2))
+            dict[name_def] = val
+            res = [(name_def, val)]
+            break
+    val_max = 1
+    while True:
+        line = lr.get()
+        if line == "end Std_Names;\n":
+            break
+        if line.endswith(":=\n"):
+            line = line.rstrip() + lr.get()
+        m = pat_name_def.match(line)
+        if m:
+            name_def = m.group(1)
+            name_ref = m.group(2)
+            val = m.group(3)
+            if not val:
+                val = 0
+            val_ref = dict.get(name_ref, None)
+            if not val_ref:
+                raise pnodes.ParseError(lr, "name {0} not found".format(name_ref))
+            val = val_ref + int(val)
+            val_max = max(val_max, val)
+            dict[name_def] = val
+            res.append((name_def, val))
+    print("class Name:")
+    for n, v in res:
+        # Avoid clash with Python names
+        if n in ["False", "True", "None"]:
+            n = "N" + n
+        print("    {0} = {1}".format(n, v))
+
+
+def do_libghdl_tokens():
+    read_enum("vhdl-tokens.ads", "Token_Type", "Tok_", "Tok")
+
+
+def do_libghdl_errorout():
+    print("from libghdl import libghdl")
+    print("\n" "Enable_Warning = libghdl.errorout__enable_warning")
+    read_enum(
+        "../errorout.ads",
+        "Msgid_Type",
+        "(Msgid|Warnid)_",
+        "Msgid",
+        g=lambda m: m.group(1) + "_" + m.group(2),
+    )
+
+
+pnodes.actions.update(
+    {
+        "class-kinds": do_class_kinds,
+        "libghdl-nodes": do_libghdl_nodes,
+        "libghdl-meta": do_libghdl_meta,
+        "libghdl-names": do_libghdl_names,
+        "libghdl-tokens": do_libghdl_tokens,
+        "libghdl-elocs": do_libghdl_elocations,
+        "libghdl-errorout": do_libghdl_errorout,
+    }
+)
+
+
+pnodes.main()
diff --git a/src/edif/Makefile b/src/edif/Makefile
index 3a3cb4a8b..e7e449483 100644
--- a/src/edif/Makefile
+++ b/src/edif/Makefile
@@ -6,8 +6,16 @@ ortho_srcdir=../ortho
 GEN_SRCS=edif-nodes.adb edif-nodes_meta.ads edif-nodes_meta.adb
 CC=gcc
 
-PNODES=../../python/xtools/pnodes.py
-PNODES_ARGS=--field-file=edif-nodes.adb.in --kind-file=edif-nodes.ads --node-file=edif-nodes.ads --template-file=edif-nodes.adb.in --meta-basename=edif-nodes_meta --kind-type=Nkind --kind-range-prefix=Nkinds_ --kind-prefix=N_ --node-type=Node
+PNODES=../../scripts/pnodes.py
+PNODES_ARGS=--field-file=edif-nodes.adb.in \
+  --kind-file=edif-nodes.ads \
+  --node-file=edif-nodes.ads \
+  --template-file=edif-nodes.adb.in \
+  --meta-basename=edif-nodes_meta \
+  --kind-type=Nkind \
+  --kind-range-prefix=Nkinds_ \
+  --kind-prefix=N_ \
+  --node-type=Node
 
 all: dump_edif
 
diff --git a/src/psl/Makefile b/src/psl/Makefile
index fdcfccaf7..9d7627263 100644
--- a/src/psl/Makefile
+++ b/src/psl/Makefile
@@ -20,11 +20,18 @@
 # be committed and distribued with the sources, so that users don't need to
 # regenerate them (and don't need to have python installed).
 
-PNODES=../../python/xtools/pnodes.py
+PNODES=../../scripts/pnodes.py
 
 DEPS=psl-nodes.ads psl-nodes.adb.in $(PNODES)
 
-PNODES_FLAGS=--field-file=psl-nodes.adb.in --kind-file=psl-nodes.ads --node-file=psl-nodes.ads --template-file=psl-nodes.adb.in --meta-basename=psl-nodes_meta --kind-type=Nkind --kind-prefix=N_ --node-type=Node
+PNODES_FLAGS=--field-file=psl-nodes.adb.in \
+  --kind-file=psl-nodes.ads \
+  --node-file=psl-nodes.ads \
+  --template-file=psl-nodes.adb.in \
+  --meta-basename=psl-nodes_meta \
+  --kind-type=Nkind \
+  --kind-prefix=N_ \
+  --node-type=Node
 
 GEN_FILES=psl-nodes.adb psl-nodes_meta.ads psl-nodes_meta.adb
 
diff --git a/src/vhdl/Makefile b/src/vhdl/Makefile
index 754f063dd..08277b4d5 100644
--- a/src/vhdl/Makefile
+++ b/src/vhdl/Makefile
@@ -20,18 +20,23 @@
 # be committed and distribued with the sources, so that users don't need to
 # regenerate them (and don't need to have python installed).
 
-PNODES=../../python/xtools/pnodes.py
-PNODESPY=../../python/xtools/pnodespy.py
+PNODES=../../scripts/pnodes.py
+PNODESPY=../../scripts/pnodespy.py
 
 DEPS=vhdl-nodes.ads vhdl-nodes.adb.in $(PNODES)
 
-GEN_FILES=vhdl-nodes.adb vhdl-nodes_meta.ads vhdl-nodes_meta.adb \
-  vhdl-elocations.adb vhdl-elocations_meta.ads vhdl-elocations_meta.adb \
-  ../../python/libghdl/thin/vhdl/nodes.py \
-  ../../python/libghdl/thin/vhdl/nodes_meta.py \
-  ../../python/libghdl/thin/vhdl/tokens.py \
-  ../../python/libghdl/thin/vhdl/elocations.py \
-  ../../python/libghdl/thin/errorout.py ../../python/libghdl/thin/std_names.py
+GEN_FILES=vhdl-nodes.adb \
+  vhdl-nodes_meta.ads \
+  vhdl-nodes_meta.adb \
+  vhdl-elocations.adb \
+  vhdl-elocations_meta.ads \
+  vhdl-elocations_meta.adb \
+  ../../pyGHDL/libghdl/vhdl/nodes.py \
+  ../../pyGHDL/libghdl/vhdl/nodes_meta.py \
+  ../../pyGHDL/libghdl/vhdl/tokens.py \
+  ../../pyGHDL/libghdl/vhdl/elocations.py \
+  ../../pyGHDL/libghdl/errorout.py \
+  ../../pyGHDL/libghdl/std_names.py
 
 NODES_FLAGS=--node-file=vhdl-nodes.ads --field-file=vhdl-nodes.adb.in \
   --template-file=vhdl-nodes.adb.in --kind-file=vhdl-nodes.ads \
@@ -73,32 +78,32 @@ vhdl-elocations_meta.adb: vhdl-elocations_meta.adb.in vhdl-elocations.ads $(DEPS
 	$(PNODES) $(ELOCATIONS_FLAGS) meta_body > $@
 	chmod -w $@
 
-../../python/libghdl/thin/vhdl/nodes.py: $(DEPS) $(PNODESPY)
+../../pyGHDL/libghdl/vhdl/nodes.py: $(DEPS) $(PNODESPY)
 	$(RM) $@
 	$(PNODESPY) $(NODES_FLAGS) libghdl-nodes > $@
 	chmod -w $@
 
-../../python/libghdl/thin/vhdl/nodes_meta.py: $(DEPS) $(PNODESPY)
+../../pyGHDL/libghdl/vhdl/nodes_meta.py: $(DEPS) $(PNODESPY)
 	$(RM) $@
 	$(PNODESPY) $(NODES_FLAGS) libghdl-meta > $@
 	chmod -w $@
 
-../../python/libghdl/thin/std_names.py: $(PNODESPY) ../std_names.ads
+../../pyGHDL/libghdl/std_names.py: $(PNODESPY) ../std_names.ads
 	$(RM) $@
 	$(PNODESPY) $(NODES_FLAGS) libghdl-names > $@
 	chmod -w $@
 
-../../python/libghdl/thin/vhdl/tokens.py: $(PNODESPY) vhdl-tokens.ads
+../../pyGHDL/libghdl/vhdl/tokens.py: $(PNODESPY) vhdl-tokens.ads
 	$(RM) $@
 	$(PNODESPY) $(NODES_FLAGS) libghdl-tokens > $@
 	chmod -w $@
 
-../../python/libghdl/thin/vhdl/elocations.py: $(PNODESPY) vhdl-elocations.ads
+../../pyGHDL/libghdl/vhdl/elocations.py: $(PNODESPY) vhdl-elocations.ads
 	$(RM) $@
 	$(PNODESPY) $(ELOCATIONS_FLAGS) libghdl-elocs > $@
 	chmod -w $@
 
-../../python/libghdl/thin/errorout.py: $(PNODESPY) ../errorout.ads
+../../pyGHDL/libghdl/errorout.py: $(PNODESPY) ../errorout.ads
 	$(RM) $@
 	$(PNODESPY) $(ELOCATIONS_FLAGS) libghdl-errorout > $@
 	chmod -w $@
-- 
cgit v1.2.3