changeset 163:8104fc8b5e90

Added visitor to c3
author Windel Bouwman
date Mon, 18 Mar 2013 20:13:57 +0100
parents d8c735dc31f9
children e023d3ce1d63
files python/c3/__init__.py python/c3/analyse.py python/c3/astnodes.py python/c3/astprinter.py python/c3/lexer.py python/c3/parser.py python/c3/scope.py python/c3/semantics.py python/c3/typecheck.py python/c3/visitor.py python/codeedit.py python/ide.py python/ppci/common.py python/testc3.py
diffstat 14 files changed, 270 insertions(+), 137 deletions(-) [+]
line wrap: on
line diff
--- a/python/c3/__init__.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/__init__.py	Mon Mar 18 20:13:57 2013 +0100
@@ -5,4 +5,5 @@
 from .typecheck import TypeChecker
 from .analyse import Analyzer
 from .codegenerator import CodeGenerator
+from .astprinter import AstPrinter
 
--- a/python/c3/analyse.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/analyse.py	Mon Mar 18 20:13:57 2013 +0100
@@ -1,5 +1,35 @@
+from .visitor import Visitor
+from .astnodes import *
 
 class Analyzer:
+   """ This class checks names and references """
    def __init__(self, diag):
       self.diag = diag
+      self.visitor = Visitor(self.a1, self.analyze)
+   def analyzePackage(self, pkg):
+      self.visitor.visit(pkg)
+   def resolveDesignator(self, d, referee=None):
+      assert type(d) is Designator
+      if d.scope.hasSymbol(d.tname):
+         s = d.scope.getSymbol(d.tname)
+         if hasattr(s, 'addRef'):
+            # TODO: make this nicer
+            s.addRef(referee)
+         return s
+      else:
+         msg = 'Cannot resolve name {0}'.format(d.tname)
+         self.diag.error(msg, d.loc)
+   def a1(self, sym):
+      pass
+   def analyze(self, sym):
+      if type(sym) is Variable:
+         sym.typ = self.resolveDesignator(sym.typ, sym)
+      elif type(sym) is VariableUse:
+         sym.target = self.resolveDesignator(sym.target, sym)
+      elif type(sym) is ProcedureCall:
+         sym.proc = self.resolveDesignator(sym.proc, sym)
+      elif type(sym) is FunctionType:
+         sym.returntype = self.resolveDesignator(sym.returntype)
+      elif type(sym) is Constant:
+         print(sym)
 
--- a/python/c3/astnodes.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/astnodes.py	Mon Mar 18 20:13:57 2013 +0100
@@ -3,19 +3,7 @@
 """
 
 class Node:
-   location = None
-   def getChildren(self):
-      children = []
-      members = dir(self)
-      for member in members:
-         member = getattr(self, member)
-         if isinstance(member, Node):
-            children.append(member)
-         elif type(member) is list:
-            for mi in member:
-               if isinstance(mi, Node):
-                  children.append(mi)
-      return children
+   pass 
 
 class Designator(Node):
    def __init__(self, tname):
@@ -26,32 +14,6 @@
 """
 Type classes
 """
-def isType(a, b):
-   """ Compare types a and b and check if they are equal """
-   if type(a) is type(b):
-      if type(a) is BaseType:
-         return (a.name == b.name) and (a.size == b.size)
-      elif type(a) is ProcedureType:
-         if len(a.parameters) != len(b.parameters):
-            print('Number of parameters does not match')
-            return False
-         for aparam, bparam in zip(a.parameters, b.parameters):
-            if not isType(aparam.typ, bparam.typ):
-               print('Parameter {0} does not match parameter {1}'.format(aparam, bparam))
-               return False
-         if a.result is None:
-            # TODO: how to handle a None return type??
-            pass
-         if not isType(a.result, b.result):
-            print('Procedure return value mismatch {0} != {1}'.format(a.result, b.result))
-            return False
-         return True
-      else:
-         print(a)
-         print(b)
-         Error('Not implemented {0}'.format(a))
-   else:
-      return False
 
 class Type(Node):
    def isType(self, b):
@@ -68,7 +30,7 @@
       self.parameters = parameters
       self.returntype = returntype
    def __repr__(self):
-      return '[PROCTYPE {0} RET {1}]'.format(self.parameters, self.returntype)
+      return '[FUNCTYPE {0} RET {1}]'.format(self.parameters, self.returntype)
 
 class DefinedType(Type):
    def __init__(self, name, typ):
@@ -79,36 +41,45 @@
 
 # Variables, parameters, local variables, constants:
 class Symbol(Node):
-   pass
+   def __init__(self, name):
+      self.name = name
+      self.refs = []
+   def addRef(self, r):
+      self.refs.append(r)
+   @property
+   def References(self):
+      return self.refs
 
 class Constant(Symbol):
-   def __init__(self, value, name=None):
-      self.name = name
+   def __init__(self, name, typ, value):
+      super().__init__(name)
+      self.typ = typ
       self.value = value
    def __repr__(self):
       return 'CONSTANT {0} = {1}'.format(self.name, self.value)
 
 class Variable(Symbol):
-   def __init__(self, name, typ):
-      self.name = name
+   def __init__(self, name, typ, ival=None):
+      super().__init__(name)
       self.typ = typ
+      self.ival = ival
       self.isLocal = False
       self.isReadOnly = False
       self.isParameter = False
    def __repr__(self):
-      return 'VAR {0} : {1}'.format(self.name, self.typ)
+      return 'VAR {0} : {1} usage: {2}'.format(self.name, self.typ, self.References)
 
 # Procedure types
 class Function(Symbol):
    """ Actual implementation of a function """
    def __init__(self, name, typ=None, block=None):
-      self.name = name
+      super().__init__(name)
       self.body = block
       self.typ = typ
    def __repr__(self):
       return 'PROCEDURE {0} {1}'.format(self.name, self.typ)
 
-# Operations:
+# Operations / Expressions:
 class Unop(Node):
    def __init__(self, a, op):
       self.a = a
@@ -122,13 +93,21 @@
       self.b = b
       self.op = op # Operation: '+', '-', '*', '/', 'mod'
    def __repr__(self):
-      return 'BINOP {0}'.format(self.op)
+      typ = self.typ if hasattr(self, 'typ') else ''
+      return 'BINOP {0} {1}'.format(self.op, typ)
 
 class VariableUse(Node):
    def __init__(self, target):
       self.target = target
    def __repr__(self):
-      return 'VAR USE {0}'.format(self.target)
+      nm = self.target.name if hasattr(self.target, 'name') else ''
+      return 'VAR USE {0}'.format(nm)
+
+class Literal(Node):
+   def __init__(self, val):
+      self.val = val
+   def __repr__(self):
+      return 'LITERAL {0}'.format(self.val)
 
 # Modules
 class Package(Node):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/c3/astprinter.py	Mon Mar 18 20:13:57 2013 +0100
@@ -0,0 +1,16 @@
+from .astnodes import *
+from .scope import *
+from .visitor import Visitor
+
+class AstPrinter:
+   def __init__(self):
+      self.visitor = Visitor(self.print1, self.print2)
+   def printAst(self, pkg):
+      self.indent = 0
+      self.visitor.visit(pkg)
+   def print1(self, node):
+      print(' ' * self.indent + str(node))
+      self.indent += 2
+   def print2(self, node):
+      self.indent -= 2
+
--- a/python/c3/lexer.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/lexer.py	Mon Mar 18 20:13:57 2013 +0100
@@ -11,7 +11,7 @@
 
 keywords = ['and', 'or', 'not','true', 'false', \
    'else', 'if', 'while', 'return', \
-   'function', 'var', 'type', \
+   'function', 'var', 'type', 'const', \
    'import', 'package' ]
 
 def tokenize(s):
@@ -62,7 +62,7 @@
            val = float(val)
          elif typ == 'STRING':
            val = val[1:-1]
-         loc = SourceLocation(line, mo.start()-line_start)
+         loc = SourceLocation(line, mo.start()-line_start, mo.end() - mo.start())
          yield Token(typ, val, loc)
        pos = mo.end()
        mo = gettok(s, pos)
--- a/python/c3/parser.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/parser.py	Mon Mar 18 20:13:57 2013 +0100
@@ -61,6 +61,8 @@
          self.parseFunctionDefinition()
       elif self.Peak == 'var':
          self.parseVarDef()
+      elif self.Peak == 'const':
+         self.parseConstDef()
       else:
          self.Error('Expected function or variable')
 
@@ -89,6 +91,19 @@
          parseVar()
       self.Consume(';')
 
+   def parseConstDef(self):
+      self.Consume('const')
+      t = self.parseType()
+      def parseConst():
+         name = self.Consume('ID')
+         self.Consume('=')
+         val = self.parseExpression()
+         self.sema.actOnConstDef(name.val, name.loc, t, val)
+      parseConst()
+      while self.hasConsumed(','):
+         parseConst()
+      self.Consume(';')
+      
    # Procedures
    def parseFunctionDefinition(self):
       self.Consume('function')
@@ -193,6 +208,9 @@
       elif self.Peak == 'NUMBER':
          val = self.Consume('NUMBER')
          return self.sema.actOnNumber(val.val, val.loc)
+      elif self.Peak == 'REAL':
+         val = self.Consume('REAL')
+         return self.sema.actOnNumber(val.val, val.loc)
       elif self.Peak == 'ID':
          d = self.parseDesignator()
          if self.Peak == '(':
--- a/python/c3/scope.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/scope.py	Mon Mar 18 20:13:57 2013 +0100
@@ -23,9 +23,17 @@
    def addSymbol(self, sym):
       self.symbols[sym.name] = sym
 
+# buildin types:
+intType = astnodes.BaseType('int')
+doubleType = astnodes.BaseType('double')
+voidType = astnodes.BaseType('void')
+boolType = astnodes.BaseType('void')
+
 def createBuiltins(scope):
-   for tn in ['int', 'u32', 'u16', 'double', 'void']:
+   for tn in ['u64', 'u32', 'u16', 'u8']:
       scope.addSymbol(astnodes.BaseType(tn))
+   for t in [intType, doubleType, voidType, boolType]:
+      scope.addSymbol(t)
 
 topScope = Scope()
 createBuiltins(topScope)
--- a/python/c3/semantics.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/semantics.py	Mon Mar 18 20:13:57 2013 +0100
@@ -19,6 +19,10 @@
       s = astnodes.Variable(name, t)
       s.loc = loc
       self.addSymbol(s)
+   def actOnConstDef(self, name, loc, t, val):
+      s = astnodes.Constant(name, t, val)
+      s.loc = loc
+      self.addSymbol(s)
    def actOnFuncDef1(self, name, loc):
       self.curFunc = astnodes.Function(name)
       self.curFunc.loc = loc
@@ -48,7 +52,7 @@
       bo.loc = loc
       return bo
    def actOnNumber(self, num, loc):
-      n = astnodes.Constant(num)
+      n = astnodes.Literal(num)
       n.loc = loc
       return n
    def actOnVariableUse(self, d):
--- a/python/c3/typecheck.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/c3/typecheck.py	Mon Mar 18 20:13:57 2013 +0100
@@ -1,61 +1,67 @@
-from .astnodes import BaseType, Variable, Designator, Function
-from .astnodes import CompoundStatement, Assignment, VariableUse
-from .astnodes import Binop, Unop, Constant
-from .astnodes import IfStatement, WhileStatement, ReturnStatement, ProcedureCall
-from .astnodes import FunctionType, BaseType
-from . import astnodes
-from .scope import topScope
+from .astnodes import *
+from .scope import *
+from .visitor import Visitor
+
+def equalTypes(a, b):
+   """ Compare types a and b for equality. Not equal until proven otherwise. """
+   if type(a) is type(b):
+      if type(a) is BaseType:
+         return a.name == b.name
+   return False
 
 class TypeChecker:
    def __init__(self, diag):
       self.diag = diag
+      self.visitor = Visitor(self.precheck, self.check2)
    def checkPackage(self, pkg):
-      for s in pkg.scope:
-         self.check(s)
-   def resolveDesignator(self, d):
-      if d.scope.hasSymbol(d.tname):
-         return d.scope.getSymbol(d.tname)
-      else:
-         msg = 'Cannot resolve name {0}'.format(d.tname)
-         self.diag.error(msg, d.loc)
-   def check(self, sym):
-      if type(sym) is Variable:
-         if type(sym.typ) is Designator:
-            sym.typ = self.resolveDesignator(sym.typ)
-      elif type(sym) is Function:
-         for s in sym.scope:
-            self.check(s)
-         self.check(sym.typ)
-         self.check(sym.body)
-      elif type(sym) is CompoundStatement:
-         for s in sym.statements:
-            self.check(s)
+      self.visitor.visit(pkg)
+   def precheck(self, sym):
+      pass
+   def check2(self, sym):
+      if type(sym) is Function:
+         pass
       elif type(sym) is IfStatement:
-         self.check(sym.condition)
-         self.check(sym.truestatement)
-         self.check(sym.falsestatement)
-      elif type(sym) is VariableUse:
-         if type(sym.target) is Designator:
-            sym.target = self.resolveDesignator(sym.target)
-      elif type(sym) is ProcedureCall:
-         if type(sym.proc) is Designator:
-            sym.proc = self.resolveDesignator(sym.proc)
+         print(sym.condition)
+         if not equalTypes(sym.condition.typ, boolType):
+            self.diag.error('Condition must be a boolean expression', sym.condition.loc)
       elif type(sym) is Assignment:
-         self.check(sym.lval)
-         self.check(sym.rval)
+         pass
       elif type(sym) is ReturnStatement:
-         self.check(sym.expr)
-      elif type(sym) is Constant:
-         if type(sym.value) is int:
-            sym.typ = topScope.getSymbol('int')
-      elif type(sym) is FunctionType:
-         if type(sym.returntype) is Designator:
-            sym.returntype = self.resolveDesignator(sym.returntype)
-         self.check(sym.returntype)
+         pass
+      elif type(sym) is ProcedureCall:
+         # Check arguments:
+
+         # determine return type:
+         sym.typ = sym.proc.typ.returntype
+      elif type(sym) is VariableUse:
+         if sym.target:
+            sym.typ = sym.target.typ
+         else:
+            sym.typ = voidType
+      elif type(sym) is Literal:
+         if type(sym.val) is int:
+            sym.typ = intType
+         elif type(sym.val) is float:
+            sym.typ = doubleType
+         else:
+            self.diag.error('Unknown literal type', sym.loc)
       elif type(sym) is Binop:
-         self.check(sym.a)
-         self.check(sym.b)
-         if type(sym.a) is Constant and type(sym.b) is Constant:
-            # Possibly fold expression
-            pass
+         if sym.op in ['+', '-', '*', '/']:
+            if equalTypes(sym.a.typ, sym.b.typ):
+               sym.typ = sym.a.typ
+            else:
+               # assume void here?
+               sym.typ = voidType
+               self.diag.error('Types unequal', sym.loc)
+         elif sym.op in ['>', '<']:
+            if equalTypes(sym.a.typ, sym.b.typ):
+               sym.typ = boolType
+            else:
+               sym.typ = voidType
+               self.diag.error('Types unequal', sym.loc)
+         else:
+            sym.typ = voidType
+            print('unknown binop', sym.op)
+      else:
+         print('Unknown type check', sym)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/c3/visitor.py	Mon Mar 18 20:13:57 2013 +0100
@@ -0,0 +1,45 @@
+from .astnodes import *
+
+class Visitor:
+   """ Visitor that visits all nodes in the ast and runs the function 'f' """
+   def __init__(self, f1, f2):
+      self.f1 = f1
+      self.f2 = f2
+   def visit(self, node):
+      # Run visitor:
+      self.f1(node)
+      # Descent into subnodes:
+      if type(node) is Package:
+         for s in node.scope:
+            self.visit(s)
+      elif type(node) is Function:
+         for s in node.scope:
+            self.visit(s)
+         self.visit(node.typ)
+         self.visit(node.body)
+      elif type(node) is CompoundStatement:
+         for s in node.statements:
+            self.visit(s)
+      elif type(node) is IfStatement:
+         self.visit(node.condition)
+         self.visit(node.truestatement)
+         self.visit(node.falsestatement)
+      elif type(node) is ProcedureCall:
+         pass
+         # TODO
+      elif type(node) is Assignment:
+         self.visit(node.lval)
+         self.visit(node.rval)
+      elif type(node) is ReturnStatement:
+         self.visit(node.expr)
+      elif type(node) is Binop:
+         self.visit(node.a)
+         self.visit(node.b)
+      elif type(node) in [EmptyStatement, Constant, VariableUse, Variable, Literal, FunctionType]:
+         # Those nodes do not have child nodes.
+         pass
+      else:
+         print('UNK visit', node)
+      self.f2(node)
+
+      
--- a/python/codeedit.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/codeedit.py	Mon Mar 18 20:13:57 2013 +0100
@@ -19,6 +19,8 @@
       self.scrollArea = scrollArea
       self.setFont(QFont('Courier', 16))
       self.setFocusPolicy(Qt.StrongFocus)
+      # TODO: only beam cursor in text area..
+      self.setCursor(Qt.IBeamCursor)
       h = QFontMetrics(self.font()).height()
       self.errorPixmap = QPixmap('error.png').scaled(h, h)
       self.blinkcursor = False
@@ -32,7 +34,8 @@
       t.start()
    def updateCursor(self):
       self.blinkcursor = not self.blinkcursor
-      self.update(self.cursorX, self.cursorY, self.charWidth, self.charHeight)
+      self.update()
+      #self.update(self.cursorX, self.cursorY, self.charWidth, self.charHeight)
    def setSource(self, src):
       self.src = src
       self.adjust()
@@ -43,8 +46,6 @@
       self.update()
    def setCursorPosition(self, c):
       self.cursorPosition = clipVal(c, 0, len(self.src))
-      self.cursorX = self.CursorCol * self.charWidth + self.xposTXT - self.charWidth
-      self.cursorY = self.CursorRow * self.charHeight - self.charHeight
       self.update()
    CursorPosition = property(lambda self: self.cursorPosition, setCursorPosition)
    @property
@@ -65,9 +66,9 @@
    def CurrentLine(self):
       return self.getRow(self.CursorRow)
    def setRowCol(self, r, c):
-      prevRows = self.Rows[:r]
+      prevRows = self.Rows[:r-1]
       txt = '\n'.join(prevRows)
-      c = clipVal(c, 1, len(self.getRow(r+1)))
+      c = clipVal(c, 1, len(self.getRow(r)))
       self.CursorPosition = len(txt) + c + 1
    def getRow(self, r):
       rows = self.Rows
@@ -76,6 +77,8 @@
          return ''
       else:
          return rows[r]
+   def showRow(self, r):
+      self.scrollArea.ensureVisible(self.xposTXT, r * self.charHeight, 4, self.charHeight)
    # Annotations:
    def addAnnotation(self, row, col, ln, msg):
       pass
@@ -104,12 +107,14 @@
          self.CursorPosition += len(curLine)
       else:
          self.CursorPosition += c
+      self.showRow(self.CursorRow)
    def GotoPrevLine(self):
       c = self.CursorCol - 1 # go to zero based
       self.CursorPosition -= c + 1 # line break char!
       curLine = self.CurrentLine
       if len(curLine) > c:
          self.CursorPosition -= len(curLine) - c
+      self.showRow(self.CursorRow)
    def paintEvent(self, event):
       # Helper variables:
       er = event.rect()
@@ -118,27 +123,39 @@
       # Background:
       painter.fillRect(er, self.palette().color(QPalette.Base))
       painter.fillRect(QRect(self.xposLNA, er.top(), 4 * chw, er.bottom() + 1), Qt.gray)
-      painter.fillRect(er.left(), (self.CursorRow - 1) * chh, er.width(), chh, Qt.yellow)
       errorPen = QPen(Qt.red, 3)
       # first and last row:
       row1 = max(int(er.top() / chh) - 1, 1)
       row2 = max(int(er.bottom() / chh) + 1, 1)
       # Draw contents:
+      ypos = row1 * chh - self.charDescent
+      curRow = self.CursorRow
+      ydt = -chh + self.charDescent
       for row in range(row1, row2 + 1):
-         ypos = row * chh - self.charDescent
+         if curRow == row:
+            painter.fillRect(self.xposTXT, ypos + ydt, er.width(), chh, Qt.yellow)
+            # cursor
+            if self.blinkcursor:
+               cursorX = self.CursorCol * self.charWidth + self.xposTXT - self.charWidth
+               cursorY = ypos + ydt
+               painter.fillRect(cursorX, cursorY, 2, chh, Qt.black)
          painter.setPen(Qt.black)
          painter.drawText(self.xposLNA, ypos, '{0}'.format(row))
          xpos = self.xposTXT
          painter.drawText(xpos, ypos, self.getRow(row))
-         for e in self.errorlist:
-            if e.loc.row == row:
-               painter.drawPixmap(self.xposERR, ypos - chh + self.charDescent, self.errorPixmap)
+         curErrors = [e for e in self.errorlist if e.loc.row == row]
+         for e in curErrors:
+               painter.drawPixmap(self.xposERR, ypos + ydt, self.errorPixmap)
                painter.setPen(errorPen)
-               x = self.xposTXT + (e.loc.col - 1) * chw
-               painter.drawLine(x, ypos+1, x + 100, ypos+1)
-      # cursor
-      if self.blinkcursor:
-         painter.fillRect(self.cursorX, self.cursorY, 2, chh, Qt.black)
+               x = self.xposTXT + (e.loc.col - 1) * chw - 2
+               wt = e.loc.length * chw + 4
+               painter.drawRoundedRect(x, ypos + ydt, wt, chh, 7, 7)
+               # print error balloon
+               painter.drawText(x, ypos + chh, e.msg)
+         if len(curErrors) > 0:
+            ypos += chh
+
+         ypos += chh
    def keyPressEvent(self, event):
       if event.matches(QKeySequence.MoveToNextChar):
          self.GotoNextChar()
@@ -174,7 +191,7 @@
       pos = event.pos()
       if pos.x() > self.xposTXT and pos.x():
          c = round((pos.x() - self.xposTXT) / self.charWidth)
-         r = int(pos.y() / self.charHeight)
+         r = int(pos.y() / self.charHeight) + 1
          self.setRowCol(r, c)
    def adjust(self):
       metrics = self.fontMetrics()
@@ -199,6 +216,8 @@
       self.setWidgetResizable(True)
       self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
       self.setFocusPolicy(Qt.NoFocus)
+      self.showRow = self.ic.showRow
+      self.setRowCol = self.ic.setRowCol
    Source = property(lambda s: s.ic.getSource(), lambda s, v: s.ic.setSource(v))
    def setErrors(self, el):
       self.ic.setErrors(el)
--- a/python/ide.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/ide.py	Mon Mar 18 20:13:57 2013 +0100
@@ -191,6 +191,8 @@
     addMenuEntry('Cascade windows', self.viewMenu, self.mdiArea.cascadeSubWindows)
     addMenuEntry('Tile windows', self.viewMenu, self.mdiArea.tileSubWindows)
 
+    sb = self.statusBar()
+
     # Load settings:
     self.settings = QSettings('windelsoft', 'lcfoside')
     self.loadSettings()
@@ -327,7 +329,7 @@
      ce = self.activeMdiChild()
      if not ce:
         return
-     ce.highlightErrorLocation(err.loc.row, err.loc.col)
+     ce.setRowCol(err.loc.row, err.loc.col)
 
   # Project loading:
 
--- a/python/ppci/common.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/ppci/common.py	Mon Mar 18 20:13:57 2013 +0100
@@ -1,6 +1,12 @@
 
 from collections import namedtuple
 
-SourceLocation = namedtuple('SourceLocation', ['row', 'col'])
+#SourceLocation = namedtuple('SourceLocation', ['row', 'col'])
+class SourceLocation:
+   def __init__(self, row, col, ln):
+      self.row = row
+      self.col = col
+      self.length = ln
+
 SourceRange = namedtuple('SourceRange', ['p1', 'p2'])
 
--- a/python/testc3.py	Sun Mar 10 11:36:55 2013 +0100
+++ b/python/testc3.py	Mon Mar 18 20:13:57 2013 +0100
@@ -4,12 +4,16 @@
 testsrc = """package test;
 
 var u32 c, d;
+var double e;
+var int f;
+
+const int A = 1337;
 
 function void test1() 
 {
-    var u32 b;
+    var u32 bdd;
     var int a = 10;
-    b = 20;
+    bd = 20;
     var int buf;
     var int i;
     i = 2;
@@ -41,31 +45,24 @@
 
 """
 
-def printAst(ast, indent=''):
-   print(indent + str(ast))
-   if isinstance(ast, c3.astnodes.Package):
-      for s in ast.scope:
-         printAst(s, indent + '  ')
-   if isinstance(ast, c3.astnodes.Function):
-      for s in ast.scope:
-         printAst(s, indent + '  ')
-   for c in ast.getChildren():
-      printAst(c, indent + '  ')
-
 def c3compile(src, diag):
    print('[0] source:')
-   #print(src)
+   print(src)
    print('[1] parsing')
+   # Structures:
    sema = c3.Semantics(diag)
    p = c3.Parser(sema, diag)
    tc = c3.TypeChecker(diag)
    al = c3.Analyzer(diag)
    cg = c3.CodeGenerator()
+   ap = c3.AstPrinter()
+
    x86gen = x86.X86CodeGen(diag)
    p.parseSource(src)
    ok = len(diag.diags) == 0
    if not ok:
       return
+   al.analyzePackage(sema.mod)
    tc.checkPackage(sema.mod)
    print('{0} errors'.format(len(diag.diags)))
 
@@ -73,6 +70,8 @@
       print('ERROR:')
       ppci.printError(testsrc, d)
    print('[2] ast:')
+   ap.printAst(sema.mod)
+
    #printAst(sema.mod)
 
    ok = len(diag.diags) == 0