add support for *_flag global variables needed by bfp_* functions
[openpower-isa.git] / src / openpower / decoder / pseudo / pagereader.py
index d5cae081c0438adebd35d72bb26dbcf6c2def979..71301f2f6d3e05154039f2b4309d5f7807a8b517 100644 (file)
@@ -44,9 +44,11 @@ this translates to:
     blank(s) (optional for convenience at end-of-page)
 """
 
+from openpower.util import log
 from collections import namedtuple, OrderedDict
 from copy import copy
 import os
+import re
 
 opfields = ("desc", "form", "opcode", "regs", "pcode", "sregs", "page")
 Ops = namedtuple("Ops", opfields)
@@ -62,8 +64,27 @@ def get_isa_dir():
     return os.path.join(fdir, "openpower", "isa")
 
 
-class ISA:
+pattern_opcode = r"[A-Za-z0-9_\.]+\.?"
+pattern_dynamic = r"[A-Za-z0-9_]+(?:\([A-Za-z0-9_]+\))*"
+pattern_static = r"[A-Za-z0-9]+\=[01]"
+regex_opcode = re.compile(f"^{pattern_opcode}$")
+regex_dynamic = re.compile(f"^{pattern_dynamic}(?:,{pattern_dynamic})*$")
+regex_static = re.compile(f"^\({pattern_static}(?:\s{pattern_static})*\)$")
+
+
+def operands(opcode, desc):
+    if desc is None:
+        return
+    desc = desc.replace("(", "")
+    desc = desc.replace(")", "")
+    desc = desc.replace(",", " ")
+    for operand in desc.split(" "):
+        operand = operand.strip()
+        if operand:
+            yield operand
+
 
+class ISA:
     def __init__(self):
         self.instr = OrderedDict()
         self.forms = {}
@@ -75,7 +96,7 @@ class ISA:
             if "swp" in pth:
                 continue
             if not pth.endswith(".mdwn"):
-                print ("warning, file not .mdwn, skipping", pth)
+                log ("warning, file not .mdwn, skipping", pth)
                 continue
             self.read_file(pth)
             continue
@@ -85,6 +106,9 @@ class ISA:
             with open(name, "w") as f:
                 f.write('\n'.join(rewrite) + '\n')
 
+    def __iter__(self):
+        yield from self.instr.items()
+
     def read_file_for_rewrite(self, fname):
         pagename = fname.split('.')[0]
         fname = os.path.join(get_isa_dir(), fname)
@@ -101,8 +125,8 @@ class ISA:
             # XXX this is braindead!  it doesn't look for the end
             # so please put ending of comments on one line:
             # <!-- line 1 comment -->
-            # <!-- line 2 comment -->
-            if l.startswith('<!--'):
+            # {some whitespace}<!-- line 2 comment -->
+            if l.strip().startswith('<!--'):
                 # print ("skipping comment", l)
                 l = lines.pop(0).rstrip()  # get first line
                 continue
@@ -144,6 +168,10 @@ class ISA:
             # get pseudocode
             while True:
                 l = lines.pop(0).rstrip()
+                if l.strip().startswith('<!--'):
+                    # print ("skipping comment", l)
+                    l = lines.pop(0).rstrip()  # get first line
+                    continue
                 rewrite.append(l)
                 if len(l) == 0:
                     break
@@ -171,7 +199,7 @@ class ISA:
             while lines:
                 l = lines.pop(0).rstrip()
                 rewrite.append(l)
-                if len(l) != 0 and not l.startswith('<!--'):
+                if len(l) != 0 and not l.strip().startswith('<!--'):
                     break
 
         return rewrite
@@ -190,6 +218,7 @@ class ISA:
         # all sections are mandatory so no need for a full LALR parser.
 
         l = lines.pop(0).rstrip()  # get first line
+        prefix_lines = 0
         while lines:
             if self.verbose:
                 print(l)
@@ -198,14 +227,16 @@ class ISA:
             # so please put ending of comments on one line:
             # <!-- line 1 comment -->
             # <!-- line 2 comment -->
-            if l.startswith('<!--'):
+            if l.strip().startswith('<!--'):
                 # print ("skipping comment", l)
                 l = lines.pop(0).rstrip()  # get next line
+                prefix_lines += 1
                 continue
 
             # Ignore blank lines before the first #
             if len(l) == 0:
                 l = lines.pop(0).rstrip()  # get next line
+                prefix_lines += 1
                 continue
 
             # expect get heading
@@ -214,47 +245,78 @@ class ISA:
 
             # whitespace expected
             l = lines.pop(0).strip()
+            prefix_lines += 1
             if self.verbose:
                 print(repr(l))
             assert len(l) == 0, ("blank line not found %s" % l)
 
             # Form expected
             l = lines.pop(0).strip()
+            prefix_lines += 1
             assert l.endswith('-Form'), ("line with -Form expected %s" % l)
             d['form'] = l.split('-')[0]
 
             # whitespace expected
             l = lines.pop(0).strip()
+            prefix_lines += 1
             assert len(l) == 0, ("blank line not found %s" % l)
 
             # get list of opcodes
-            li = []
+            opcodes = []
             while True:
                 l = lines.pop(0).strip()
+                prefix_lines += 1
                 if len(l) == 0:
                     break
                 assert l.startswith('*'), ("* not found in line %s" % l)
-                l = l[1:].split(' ')  # lose star
-                l = filter(lambda x: len(x) != 0, l)  # strip blanks
-                li.append(list(l))
-            opcodes = li
+                rest = l[1:].strip()
+
+                (opcode, _, rest) = map(str.strip, rest.partition(" "))
+                if regex_opcode.match(opcode) is None:
+                    raise IOError(repr(opcode))
+                opcode = [opcode]
+
+                (dynamic, _, rest) = map(str.strip, rest.partition(" "))
+                if regex_dynamic.match(dynamic) is None and dynamic:
+                    raise IOError(f"{l!r}: {dynamic!r}")
+                if dynamic:
+                    opcode.append(dynamic.split(","))
+
+                static = rest
+                if regex_static.match(static) is None and static:
+                    raise IOError(f"{l!r}: {static!r}")
+                if static:
+                    opcode.extend(static[1:-1].split(" "))
+
+                opcodes.append(opcode)
 
             # "Pseudocode" expected
             l = lines.pop(0).rstrip()
+            prefix_lines += 1
             assert l.startswith("Pseudo-code:"), ("pseudocode found %s" % l)
 
             # whitespace expected
             l = lines.pop(0).strip()
+            prefix_lines += 1
             if self.verbose:
                 print(repr(l))
             assert len(l) == 0, ("blank line not found %s" % l)
 
             # get pseudocode
-            li = []
+
+            # fix parser line numbers by prepending the right number of
+            # blank lines to the parser input
+            li = [""] * prefix_lines
+            li += [l[4:]]  # first line detected with 4-space
             while True:
                 l = lines.pop(0).rstrip()
+                prefix_lines += 1
                 if len(l) == 0:
+                    li.append(l)
                     break
+                if l.strip().startswith('<!--'):
+                    li.append("")
+                    continue
                 assert l.startswith('    '), ("4spcs not found in line %s" % l)
                 l = l[4:]  # lose 4 spaces
                 li.append(l)
@@ -262,16 +324,19 @@ class ISA:
 
             # "Special Registers Altered" expected
             l = lines.pop(0).rstrip()
+            prefix_lines += 1
             assert l.startswith("Special"), ("special not found %s" % l)
 
             # whitespace expected
             l = lines.pop(0).strip()
+            prefix_lines += 1
             assert len(l) == 0, ("blank line not found %s" % l)
 
             # get special regs
             li = []
             while lines:
                 l = lines.pop(0).rstrip()
+                prefix_lines += 1
                 if len(l) == 0:
                     break
                 assert l.startswith('    '), ("4spcs not found in line %s" % l)
@@ -283,18 +348,17 @@ class ISA:
             for o in opcodes:
                 self.add_op(o, d)
 
-            # expect and drop whitespace
+            # expect and drop whitespace and comments
             while lines:
                 l = lines.pop(0).rstrip()
-                if len(l) != 0 and not l.startswith('<!--'):
+                prefix_lines += 1
+                if len(l) != 0 and not l.strip().startswith('<!--'):
                     break
 
     def add_op(self, o, d):
         opcode, regs = o[0], o[1:]
         op = copy(d)
         op['regs'] = regs
-        if len(regs) != 0:
-            regs[0] = regs[0].split(",")
         op['opcode'] = opcode
         self.instr[opcode] = Ops(**op)