changeset 309:68b01c8abf8a

Added start of ir read and write
author Windel Bouwman
date Fri, 13 Dec 2013 13:51:02 +0100
parents 2e7f55319858
children e95e5572cd6d
files examples/pi/add.pi python/ppci/ir.py python/ppci/irutils.py test/testcg.py test/testir.py
diffstat 5 files changed, 190 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/examples/pi/add.pi	Fri Dec 13 11:53:29 2013 +0100
+++ b/examples/pi/add.pi	Fri Dec 13 13:51:02 2013 +0100
@@ -1,3 +1,5 @@
+
+module addmod
 
 function i32 add(i32 a, i32 b)
  init:
--- a/python/ppci/ir.py	Fri Dec 13 11:53:29 2013 +0100
+++ b/python/ppci/ir.py	Fri Dec 13 13:51:02 2013 +0100
@@ -6,16 +6,15 @@
     """ Container unit for variables and functions. """
     def __init__(self, name):
         self.name = name
-        self.funcs = []
+        self.functions = []
         self.variables = []
 
     def __repr__(self):
-        return 'IR-module [{0}]'.format(self.name)
+        return 'module {0}'.format(self.name)
 
-    def addFunc(self, f):
-        self.funcs.append(f)
-
-    addFunction = addFunc
+    def add_function(self, f):
+        """ Add a function to this module """
+        self.functions.append(f)
 
     def addVariable(self, v):
         self.variables.append(v)
@@ -26,7 +25,7 @@
     Variables = property(getVariables)
 
     def getFunctions(self):
-        return self.funcs
+        return self.functions
 
     Functions = property(getFunctions)
 
@@ -38,13 +37,6 @@
 
     getFunction = findFunction
 
-    def dump(self, indent='   '):
-        print(self)
-        for v in self.Variables:
-            print(indent, v)
-        for fn in self.Functions:
-            fn.dump(indent=indent+'   ')
-
     # Analysis functions:
     def check(self):
         """ Perform sanity check on module """
@@ -54,7 +46,7 @@
 
 class Function:
     """ Represents a function. """
-    def __init__(self, name):
+    def __init__(self, name, module=None):
         self.name = name
         self.entry = Block('{}_entry'.format(name))
         self.entry.function = self
@@ -64,13 +56,15 @@
         self.return_value = Temp('{}_retval'.format(name))
         self.arguments = []
         self.localvars = []
+        if module:
+            module.add_function(self)
 
     def __repr__(self):
         args = ','.join(str(a) for a in self.arguments)
-        return 'Function {}({})'.format(self.name, args)
+        return 'function i32 {}({})'.format(self.name, args)
 
-    def addBlock(self, bb):
-        self.bbs.append(bb)
+    def add_block(self, bb):
+        #self.bbs.append(bb)
         bb.function = self
 
     def removeBlock(self, bb):
@@ -118,13 +112,6 @@
         assert type(l) is LocalVariable
         self.localvars.append(l)
 
-    def dump(self, indent=''):
-        print(indent+str(self))
-        for bb in self.Blocks:
-            print(indent+'   ' + str(bb))
-            for ins in bb.Instructions:
-                print(indent + '   ' * 2 + str(ins))
-
 
 class Block:
     """
@@ -138,7 +125,7 @@
     parent = property(lambda s: s.function)
 
     def __repr__(self):
-        return 'Block {0}'.format(self.name)
+        return '{0}:'.format(self.name)
 
     def addInstruction(self, i):
         i.parent = self
@@ -272,10 +259,12 @@
 
 
 def Mul(a, b):
+    """ Multiply a by b """
     return Binop(a, '*', b)
 
 
 def Div(a, b):
+    """ Divide a in b pieces """
     return Binop(a, '/', b)
 
 
--- a/python/ppci/irutils.py	Fri Dec 13 11:53:29 2013 +0100
+++ b/python/ppci/irutils.py	Fri Dec 13 13:51:02 2013 +0100
@@ -2,7 +2,9 @@
 """
     Some utilities for ir-code.
 """
+import re
 from .ir import Temp, Block, Function, Statement
+from . import ir
 
 def dumpgv(m, outf):
     print('digraph G ', file=outf)
@@ -22,6 +24,150 @@
     print('}', file=outf)
 
 
+class Writer:
+    def write(self, ir, f):
+        """ Write ir-code to file f """
+        print(ir, file=f)
+        for v in ir.Variables:
+            print(str(v), file=f)
+        for fn in ir.Functions:
+            args = ','.join('i32 ' + str(a) for a in fn.arguments)
+            print('function i32 {}({})'.format(fn.name, args), file=f)
+            for bb in fn.Blocks:
+                print(' ' + str(bb), file=f)
+                for ins in bb.Instructions:
+                    print('  ' + str(ins), file=f)
+
+
+class IrParseException(Exception):
+    pass
+
+
+class Reader:
+    def read(self, f):
+        """ Read ir code from file f """
+        # Read lines from the file:
+        lines = [line.rstrip() for line in f]
+
+        # Create a regular expression for the lexing part:
+        tok_spec = [
+           ('NUMBER', r'\d+'),
+           ('ID', r'[A-Za-z][A-Za-z\d_]*'),
+           ('SKIP2', r'  '),
+           ('SKIP1', r' '),
+           ('OTHER', r'[\.,=:;\-+*\[\]/\(\)]|>|<|{|}|&|\^|\|')
+            ]
+        tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec)
+        gettok = re.compile(tok_re).match
+
+        def tokenize():
+            for line in lines:
+                if not line:
+                    continue  # Skip empty lines
+                mo = gettok(line)
+                first = True
+                while mo:
+                    typ = mo.lastgroup
+                    val = mo.group(typ)
+                    if typ == 'ID':
+                        if val in ['function', 'module']:
+                            typ = val
+                        yield (typ, val)
+                    elif typ == 'OTHER':
+                        typ = val
+                        yield (typ, val)
+                    elif typ in ['SKIP1', 'SKIP2']:
+                        if first:
+                            yield (typ, val)
+                    elif typ == 'NUMBER':
+                        yield (typ, int(val))
+                    else:
+                        raise NotImplementedError(str(typ))
+                    first = False
+                    pos = mo.end()
+                    mo = gettok(line, pos)
+                if len(line) != pos:
+                    raise IrParseException('Lex fault')
+                yield ('eol', 'eol')
+            yield ('eof', 'eof')
+        self.tokens = tokenize()
+        self.token = self.tokens.__next__()
+
+        try:
+            module = self.parse_module()
+            return module
+        except IrParseException as e:
+            print(e)
+
+    def next_token(self):
+        t = self.token
+        if t[0] != 'eof':
+            self.token = self.tokens.__next__()
+        return t
+
+    @property
+    def Peak(self):
+        return self.token[0]
+
+    def Consume(self, typ):
+        if self.Peak == typ:
+            return self.next_token()
+        else:
+            raise IrParseException('Expected "{}" got "{}"'.format(typ, self.Peak))
+        
+    def parse_module(self):
+        """ Entry for recursive descent parser """
+        self.Consume('module')
+        name = self.Consume('ID')[1]
+        module = ir.Module(name)
+        self.Consume('eol')
+        while self.Peak != 'eof':
+            if self.Peak == 'function':
+                module.add_function(self.parse_function())
+            else:
+                raise IrParseException('Expected function got {}'.format(self.Peak))
+        return module
+                
+    def parse_function(self):
+        self.Consume('function')
+        self.parse_type()
+        name = self.Consume('ID')[1]
+        function = ir.Function(name)
+        self.Consume('(')
+        while self.Peak != ')':
+            self.parse_type()
+            self.Consume('ID')
+            if self.Peak != ',':
+                break
+            else:
+                self.Consume(',')
+        self.Consume(')')
+        self.Consume('eol')
+        while self.Peak == 'SKIP1':
+            function.add_block(self.parse_block())
+        return function
+
+    def parse_type(self):
+        self.Consume('ID')
+
+    def parse_block(self):
+        self.Consume('SKIP1')
+        name = self.Consume('ID')[1]
+        block = ir.Block(name)
+        self.Consume(':')
+        self.Consume('eol')
+        while self.Peak == 'SKIP2':
+            self.parse_statement()
+        return block
+
+    def parse_statement(self):
+        self.Consume('SKIP2')
+        while self.Peak != 'eol':
+            # raise NotImplementedError()
+            self.next_token()
+        self.Consume('eol')
+
+
 # Constructing IR:
 
 class NamedClassGenerator:
@@ -61,7 +207,7 @@
 
     def newFunction(self, name):
         f = Function(name)
-        self.m.addFunc(f)
+        self.m.add_function(f)
         return f
 
     def newBlock(self):
--- a/test/testcg.py	Fri Dec 13 11:53:29 2013 +0100
+++ b/test/testcg.py	Fri Dec 13 13:51:02 2013 +0100
@@ -9,7 +9,7 @@
 def genTestFunction():
     m = ir.Module('tst')
     f = ir.Function('tst')
-    m.addFunction(f)
+    m.add_function(f)
     return m, f, f.entry
 
 
--- a/test/testir.py	Fri Dec 13 11:53:29 2013 +0100
+++ b/test/testir.py	Fri Dec 13 13:51:02 2013 +0100
@@ -1,6 +1,7 @@
 import unittest
 import os
 import sys
+import io
 import ppci
 from ppci import ir
 from ppci import irutils
@@ -79,11 +80,32 @@
         self.b.setFunction(f)
         self.b.setBlock(self.b.newBlock())
         v1 = ir.Const(0)
-        v2 = ir.Const(0)
-        v3 = ir.Add(v1, v2)
+        v3 = ir.Add(v1, ir.Const(0))
+
+class TestWriter(unittest.TestCase):
+    def testWrite(self):
+        writer = irutils.Writer()
+        module = ir.Module('mod1')
+        function = ir.Function('func1', module)
+        f = io.StringIO()
+        writer.write(module, f)
+        #print(f.getvalue())
+        f2 = io.StringIO(f.getvalue())
+        reader = irutils.Reader()
+        module2 = reader.read(f2)
+        f = io.StringIO()
+        writer.write(module2, f)
+        #print(f.getvalue())
 
 
-
+class TestReader(unittest.TestCase):
+    def testAddExample(self):
+        reader = irutils.Reader()
+        with open('../examples/pi/add.pi') as f:
+            m = reader.read(f)
+            self.assertTrue(m)
+            #print(m)
+        
 
 if __name__ == '__main__':
     unittest.main()