# HG changeset patch # User Windel Bouwman # Date 1386612021 -3600 # Node ID b145f8e6050ba487029c6a2be6072d5c7db57238 # Parent 0615b53087100c853fd0819423d060a35aaa009d Start on c3 rewrite diff -r 0615b5308710 -r b145f8e6050b doc/compiler.rst --- a/doc/compiler.rst Fri Dec 06 13:50:38 2013 +0100 +++ b/doc/compiler.rst Mon Dec 09 19:00:21 2013 +0100 @@ -64,12 +64,16 @@ .. autoclass:: ppci.c3.Lexer -.. autoclass:: ppci.c3.Builder - .. autoclass:: ppci.c3.Parser +.. autoclass:: ppci.c3.Analyzer + +.. autoclass:: ppci.c3.TypeChecker + .. autoclass:: ppci.c3.CodeGenerator +.. autoclass:: ppci.c3.Builder + Back-end -------- @@ -80,7 +84,7 @@ 3. Peep hole optimization? 4. real code generation -.. automodule:: codegen +.. automodule:: ppci.codegen :members: Instruction selection diff -r 0615b5308710 -r b145f8e6050b doc/index.rst --- a/doc/index.rst Fri Dec 06 13:50:38 2013 +0100 +++ b/doc/index.rst Mon Dec 09 19:00:21 2013 +0100 @@ -17,6 +17,9 @@ ir +>>> from utils import HexFile +>>> h = HexFile() + Indices and tables ================== diff -r 0615b5308710 -r b145f8e6050b examples/c3/burn2.c3 --- a/examples/c3/burn2.c3 Fri Dec 06 13:50:38 2013 +0100 +++ b/examples/c3/burn2.c3 Mon Dec 09 19:00:21 2013 +0100 @@ -27,14 +27,14 @@ return; } - var stm32f4xx:RCC_Type RCC; - RCC = cast(0x40023800); + var stm32f4xx.RCC_Type RCC; + RCC = cast(0x40023800); // Enable the clock to port D: RCC->AHB1ENR = RCC->AHB1ENR | (1 << 3); // Memory mapped control registers: - var stm32f4xx:GPIO_Type GPIOD; - GPIOD = cast(0x40020C00); + var stm32f4xx.GPIO_Type GPIOD; + GPIOD = cast(0x40020C00); // PD13 == output (01) GPIOD->MODER = (1 << (pin << 1)); @@ -48,7 +48,7 @@ init(13); var int a; - a = 0 + a = 0; while (a < 1000) { a = add(a, 1); diff -r 0615b5308710 -r b145f8e6050b kernel/make.py --- a/kernel/make.py Fri Dec 06 13:50:38 2013 +0100 +++ b/kernel/make.py Mon Dec 09 19:00:21 2013 +0100 @@ -5,10 +5,15 @@ sys.path.insert(0, os.path.join('..', 'python')) import zcc -arglist = ['memory.c3', 'kernel.c3', 'syscall.c3', 'process.c3', 'schedule.c3', 'arch_arm.c3'] -arglist += ['--target', 'arm'] -arglist += ['--dumpasm', '--dumpir'] -arglist += ['--log', 'debug'] +def make(): + arglist = ['memory.c3', 'kernel.c3', 'syscall.c3', 'process.c3'] + arglist += ['schedule.c3', 'arch_arm.c3'] + arglist += ['--target', 'arm'] + arglist += ['--dumpasm', '--dumpir'] + arglist += ['--log', 'debug'] -args = zcc.parser.parse_args(arglist) -zcc.main(args) + args = zcc.parser.parse_args(arglist) + zcc.main(args) + +if __name__ == '__main__': + make() diff -r 0615b5308710 -r b145f8e6050b kernel/process.c3 --- a/kernel/process.c3 Fri Dec 06 13:50:38 2013 +0100 +++ b/kernel/process.c3 Mon Dec 09 19:00:21 2013 +0100 @@ -13,8 +13,8 @@ // List procs; // init is the root of all processes: -var process_t* init_pid = 0; -var int next_pid = 0; +var process_t* init_pid; +var int next_pid; function void init() { diff -r 0615b5308710 -r b145f8e6050b python/ide/astviewer.py --- a/python/ide/astviewer.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ide/astviewer.py Mon Dec 09 19:00:21 2013 +0100 @@ -1,6 +1,6 @@ from PyQt4.QtCore import * from PyQt4.QtGui import * -from c3 import Visitor, astnodes +from ppci.c3 import Visitor, astnodes class AstModelBuilder: def __init__(self): diff -r 0615b5308710 -r b145f8e6050b python/ide/ide.py --- a/python/ide/ide.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ide/ide.py Mon Dec 09 19:00:21 2013 +0100 @@ -20,7 +20,6 @@ from logview import LogView as BuildOutput from disasm import Disassembly stutil = __import__('st-util') -import c3 import zcc import outstream from target import armtarget @@ -172,7 +171,6 @@ self.settings = QSettings('windelsoft', 'lcfoside') self.loadSettings() self.diag = ppci.DiagnosticsManager() - self.c3front = c3.Builder(self.diag) # File handling: def newFile(self): diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/analyse.py --- a/python/ppci/c3/analyse.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/analyse.py Mon Dec 09 19:00:21 2013 +0100 @@ -1,35 +1,40 @@ import logging -from .visitor import Visitor +from .visitor import Visitor, AstPrinter from .astnodes import * -from .scope import * +from .scope import Scope class C3Pass: def __init__(self, diag): self.diag = diag self.logger = logging.getLogger('c3') - self.ok = True self.visitor = Visitor() def error(self, msg, loc=None): - self.ok = False + self.pkg.ok = False self.diag.error(msg, loc) def visit(self, pkg, pre, post): + self.pkg = pkg self.visitor.visit(pkg, pre, post) + self.pkg = None class AddScope(C3Pass): + scoped_types = [Package, Function] + def __init__(self, diag, topScope): + super().__init__(diag) + self.topScope = topScope + """ Scope is attached to the correct modules. """ def addScope(self, pkg): self.logger.info('Adding scoping to package {}'.format(pkg.name)) # Prepare top level scope and set scope to all objects: - self.scopeStack = [topScope] + self.scopeStack = [self.topScope] modScope = Scope(self.CurrentScope) self.scopeStack.append(modScope) self.visit(pkg, self.enterScope, self.quitScope) assert len(self.scopeStack) == 2 - return self.ok @property def CurrentScope(self): @@ -46,18 +51,18 @@ sym.scope = self.CurrentScope # Add symbols to current scope: - if isinstance(sym, Symbol) or isinstance(sym, DefinedType): + if isinstance(sym, Symbol): self.addSymbol(sym) # Create subscope: - if type(sym) in [Package, Function]: + if type(sym) in self.scoped_types: newScope = Scope(self.CurrentScope) self.scopeStack.append(newScope) sym.innerScope = self.CurrentScope def quitScope(self, sym): # Pop out of scope: - if type(sym) in [Package, Function]: + if type(sym) in self.scoped_types: self.scopeStack.pop(-1) @@ -69,8 +74,8 @@ """ def analyzePackage(self, pkg, packageDict): - self.ok = True # Prepare top level scope and set scope to all objects: + self.pkg = pkg self.logger.info('Resolving imports for package {}'.format(pkg.name)) # Handle imports: @@ -82,74 +87,12 @@ pkg.scope.addSymbol(ip) fr = FixRefs(self.diag) fr.fixRefs(pkg) - return self.ok and fr.ok class FixRefs(C3Pass): def fixRefs(self, pkg): - self.ok = True self.logger.info('Resolving references for {}'.format(pkg.name)) - self.visitor.visit(pkg, self.findRefs) - - # Reference fixups: - def resolveDesignator(self, d, scope): - assert isinstance(d, Designator), type(d) - assert type(scope) is Scope - if scope.hasSymbol(d.tname): - s = scope.getSymbol(d.tname) - if isinstance(d, ImportDesignator): - if s.innerScope.hasSymbol(d.vname): - return s.innerScope.getSymbol(d.vname) - else: - s.addRef(None) # TODO: fix this? - return s - self.error('Cannot resolve name {0}'.format(d.tname), d.loc) - - def resolveType(self, t, scope): - if type(t) is PointerType: - t.ptype = self.resolveType(t.ptype, scope) - return t - elif type(t) is StructureType: - offset = 0 - for mem in t.mems: - mem.offset = offset - mem.typ = self.resolveType(mem.typ, scope) - offset += theType(mem.typ).bytesize - t.bytesize = offset - return t - elif isinstance(t, Designator): - t = self.resolveDesignator(t, scope) - if t: - return self.resolveType(t, scope) - elif isinstance(t, Type): - # Already resolved?? - return t - else: - raise Exception('Error resolving type {} {}'.format(t, type(t))) - - def findRefs(self, sym): - if type(sym) is Constant or isinstance(sym, Variable): - sym.typ = self.resolveType(sym.typ, sym.scope) - elif type(sym) is TypeCast: - sym.to_type = self.resolveType(sym.to_type, sym.scope) - elif type(sym) is VariableUse: - sym.target = self.resolveDesignator(sym.target, sym.scope) - elif type(sym) is FunctionCall: - varuse = sym.proc - if type(varuse) is VariableUse: - sym.proc = self.resolveDesignator(varuse.target, sym.scope) - elif type(sym) is Function: - # Checkup function type: - ft = sym.typ - ft.returntype = self.resolveType(ft.returntype, sym.scope) - ft.parametertypes = [self.resolveType(pt, sym.scope) for pt in - ft.parametertypes] - # Mark local variables: - for d in sym.declarations: - if isinstance(d, Variable): - d.isLocal = True - elif type(sym) is DefinedType: - sym.typ = self.resolveType(sym.typ, sym.scope) + self.pkg = pkg # Type checking: @@ -158,13 +101,18 @@ """ Recurse until a 'real' type is found """ if type(t) is DefinedType: return theType(t.typ) + elif type(t) is Identifier: + return theType(resolveSymbol(t)) return t def equalTypes(a, b): """ Compare types a and b for structural equavalence. """ # Recurse into named types: - a, b = theType(a), theType(b) + a = theType(a) + b = theType(b) + assert isinstance(a, Type) + assert isinstance(b, Type) if type(a) is type(b): if type(a) is BaseType: @@ -177,8 +125,7 @@ return all(equalTypes(am.typ, bm.typ) for am, bm in zip(a.mems, b.mems)) else: - raise NotImplementedError('Type compare for {} not implemented' - .format(type(a))) + raise NotImplementedError('{} not implemented'.format(type(a))) return False @@ -187,7 +134,8 @@ toT = theType(toT) if isinstance(fromT, PointerType) and isinstance(toT, PointerType): return True - elif fromT is intType and isinstance(toT, PointerType): + elif type(fromT) is BaseType and fromT.name == 'int' and \ + isinstance(toT, PointerType): return True return False @@ -196,20 +144,121 @@ # TODO: solve this better s.expect_rvalue = True +# Reference fixups: +def resolveDesignator(self, d, scope): + assert isinstance(d, Designator), type(d) + assert type(scope) is Scope + if d.tname in scope: + s = scope[d.tname] + if isinstance(d, ImportDesignator): + if s.innerScope.hasSymbol(d.vname): + return s.innerScope.getSymbol(d.vname) + else: + s.addRef(None) # TODO: fix this? + return s + self.error('Cannot resolve name {0}'.format(d.tname), d.loc) + + +def findRefs(self, sym): + if type(sym) is Constant or isinstance(sym, Variable): + sym.typ = self.resolveType(sym.typ, sym.scope) + elif type(sym) is TypeCast: + sym.to_type = self.resolveType(sym.to_type, sym.scope) + elif type(sym) is VariableUse: + sym.target = self.resolveDesignator(sym.target, sym.scope) + elif type(sym) is FunctionCall: + varuse = sym.proc + if type(varuse) is VariableUse: + sym.proc = self.resolveDesignator(varuse.target, sym.scope) + elif type(sym) is Function: + # Checkup function type: + ft = sym.typ + ft.returntype = self.resolveType(ft.returntype, sym.scope) + ft.parametertypes = [self.resolveType(pt, sym.scope) for pt in + ft.parametertypes] + # Mark local variables: + for d in sym.declarations: + if isinstance(d, Variable): + d.isLocal = True + elif type(sym) is DefinedType: + sym.typ = self.resolveType(sym.typ, sym.scope) + + +def resolveSymbol(sym): + name = sym.target + scope = sym.scope + return scope[name] + class TypeChecker(C3Pass): + """ Checks if all types are correctly used """ def checkPackage(self, pkg): self.logger.info('Type checking {}'.format(pkg.name)) - self.ok = True - self.visit(pkg, None, self.check2) - return self.ok + self.pkg = pkg + AstPrinter().printAst(pkg) + # Prepare some standard types: + self.intType = pkg.scope['int'] + self.boolType = pkg.scope['bool'] + for decl in pkg.declarations: + self.checkDecl(decl) + self.pkg.ok = False + + def checkDecl(self, decl): + if type(decl) is Function: + self.checkFunction(decl) + elif isinstance(decl, Variable): + pass + elif isinstance(decl, Type): + self.checkType(decl) + else: + raise Exception(str(decl)) - def check2(self, sym): - if type(sym) in [IfStatement, WhileStatement]: - if not equalTypes(sym.condition.typ, boolType): - msg = 'Condition must be of type {}'.format(boolType) + def checkType(self, t): + if type(t) is PointerType: + self.checkType(t.ptype) + elif type(t) is StructureType: + offset = 0 + for mem in t.mems: + mem.offset = offset + self.checkType(mem.typ) + offset += theType(mem.typ).bytesize + t.bytesize = offset + elif type(t) is DefinedType: + self.checkType(t.typ) + elif isinstance(t, Identifier): + pass + elif isinstance(t, Member): + pass + else: + raise Exception('Error resolving type {} {}'.format(t, type(t))) + + def checkVariable(self, v): + self.checkType(v.typ) + + def checkFunction(self, f): + for decl in f.declarations: + self.checkDecl(decl) + self.checkStmt(f.body) + + + def checkStmt(self, sym): + assert isinstance(sym, Statement) + if type(sym) is IfStatement: + self.checkExpr(sym.condition) + if not equalTypes(sym.condition.typ, self.boolType): + msg = 'Condition must be boolean' self.error(msg, sym.condition.loc) + self.checkStmt(sym.truestatement) + self.checkStmt(sym.falsestatement) + elif type(sym) is WhileStatement: + self.checkExpr(sym.condition) + if not equalTypes(sym.condition.typ, self.boolType): + msg = 'Condition must be boolean' + self.error(msg, sym.condition.loc) + self.checkStmt(sym.statement) elif type(sym) is Assignment: + self.checkExpr(sym.lval) + self.checkExpr(sym.rval) l, r = sym.lval, sym.rval if not equalTypes(l.typ, r.typ): msg = 'Cannot assign {} to {}'.format(r.typ, l.typ) @@ -221,9 +270,25 @@ expectRval(sym.rval) elif type(sym) is ReturnStatement: pass - elif type(sym) is FunctionCall: + elif type(sym) is EmptyStatement: + pass + elif type(sym) is ExpressionStatement: + self.checkExpr(sym.ex) + elif type(sym) is CompoundStatement: + for s in sym.statements: + self.checkStmt(s) + else: + raise Exception(str(sym)) + + def checkExpr(self, sym): + assert isinstance(sym, Expression) + if type(sym) is FunctionCall: # Check arguments: + for arg in sym.args: + self.checkExpr(arg) + self.checkExpr(sym.proc) ngiv = len(sym.args) + # tg = ptypes = sym.proc.typ.parametertypes nreq = len(ptypes) if ngiv != nreq: @@ -237,18 +302,16 @@ .format(a.typ, at), a.loc) # determine return type: sym.typ = sym.proc.typ.returntype - elif type(sym) is VariableUse: + elif type(sym) is Identifier: sym.lvalue = True - assert isinstance(sym.target, Variable), sym.target - sym.typ = sym.target.typ + tg = resolveSymbol(sym) + sym.kind = type(tg) + sym.typ = tg.typ elif type(sym) is Literal: sym.lvalue = False - if type(sym.val) is int: - sym.typ = intType - elif type(sym.val) is float: - sym.typ = doubleType - elif type(sym.val) is bool: - sym.typ = boolType + typemap = {int: 'int', float: 'double', bool: 'bool'} + if type(sym.val) in typemap: + sym.typ = self.pkg.scope[typemap[type(sym.val)]] else: raise Exception('Unknown literal type'.format(sym.val)) elif type(sym) is Unop: @@ -261,14 +324,16 @@ # pointer deref sym.lvalue = True # check if the to be dereferenced variable is a pointer type: + self.checkExpr(sym.ptr) ptype = theType(sym.ptr.typ) if type(ptype) is PointerType: sym.typ = ptype.ptype else: self.error('Cannot dereference non-pointer type {}' .format(ptype), sym.loc) - sym.typ = intType - elif type(sym) is FieldRef: + sym.typ = self.intType + elif type(sym) is Member: + self.checkExpr(sym.base) basetype = sym.base.typ sym.lvalue = sym.base.lvalue basetype = theType(basetype) @@ -278,59 +343,61 @@ else: self.error('{} does not contain field {}' .format(basetype, sym.field), sym.loc) - sym.typ = intType + sym.typ = self.intType else: self.error('Cannot select field {} of non-structure type {}' .format(sym.field, basetype), sym.loc) - sym.typ = intType + sym.typ = self.intType elif type(sym) is Binop: + self.checkExpr(sym.a) + self.checkExpr(sym.b) sym.lvalue = False if sym.op in ['+', '-', '*', '/', '<<', '>>', '|', '&']: expectRval(sym.a) expectRval(sym.b) if equalTypes(sym.a.typ, sym.b.typ): - if equalTypes(sym.a.typ, intType): + if equalTypes(sym.a.typ, self.intType): sym.typ = sym.a.typ else: self.error('Can only add integers', sym.loc) - sym.typ = intType + sym.typ = self.intType else: # assume void here? TODO: throw exception! - sym.typ = intType + sym.typ = self.intType self.error('Types unequal {} != {}' .format(sym.a.typ, sym.b.typ), sym.loc) elif sym.op in ['>', '<', '==', '<=', '>=', '!=']: expectRval(sym.a) expectRval(sym.b) - sym.typ = boolType + sym.typ = self.boolType if not equalTypes(sym.a.typ, sym.b.typ): self.error('Types unequal {} != {}' .format(sym.a.typ, sym.b.typ), sym.loc) elif sym.op in ['or', 'and']: - sym.typ = boolType - if not equalTypes(sym.a.typ, boolType): - self.error('Must be {0}'.format(boolType), sym.a.loc) - if not equalTypes(sym.b.typ, boolType): - self.error('Must be {0}'.format(boolType), sym.b.loc) + sym.typ = self.boolType + if not equalTypes(sym.a.typ, self.boolType): + self.error('Must be boolean', sym.a.loc) + if not equalTypes(sym.b.typ, self.boolType): + self.error('Must be boolean', sym.b.loc) else: raise Exception('Unknown binop {0}'.format(sym.op)) elif isinstance(sym, Variable): - # check initial value type: - # TODO pass elif type(sym) is TypeCast: + self.checkExpr(sym.a) + self.checkType(sym.to_type) if canCast(sym.a.typ, sym.to_type): sym.typ = sym.to_type else: self.error('Cannot cast {} to {}' .format(sym.a.typ, sym.to_type), sym.loc) - sym.typ = intType + sym.typ = self.intType elif type(sym) is Constant: if not equalTypes(sym.typ, sym.value.typ): self.error('Cannot assign {0} to {1}' .format(sym.value.typ, sym.typ), sym.loc) elif type(sym) in [CompoundStatement, Package, Function, FunctionType, - ExpressionStatement, DefinedType]: + ExpressionStatement, DefinedType, EmptyStatement]: pass else: raise NotImplementedError('Unknown type check {0}'.format(sym)) diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/astnodes.py --- a/python/ppci/c3/astnodes.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/astnodes.py Mon Dec 09 19:00:21 2013 +0100 @@ -9,11 +9,14 @@ class Node: + """ Base class of all nodes in a AST """ pass # Variables, parameters, local variables, constants and named types: class Symbol(Node): + """ Symbol is the base class for all named things like variables, + functions, constants and types and modules """ def __init__(self, name): self.name = name self.refs = [] @@ -38,47 +41,21 @@ return 'MODULE {}'.format(self.name) -class Designator(Node): - def __init__(self, tname, loc): - self.tname = tname - self.loc = loc - - def __repr__(self): - return 'DESIGNATOR {}'.format(self.tname) - - -class ImportDesignator(Designator): - def __init__(self, tname, vname, loc): - super().__init__(tname, loc) - self.vname = vname - - def __repr__(self): - return 'IMPORT DESIGNATOR {}:{}'.format(self.tname, self.vname) - - -""" -Type classes - -types must be comparable. - -There are the following types: -- base type -> basic type (built in) -- struct type -> a composite type that contains a list of named fields - of other types -- function type -""" - - class Type(Node): + """ Base class of all types """ pass class NamedType(Type, Symbol): + """ Some types are named, for example a user defined type (typedef) + and built in types. That is why this class derives from both Type + and Symbol. """ def __init__(self, name): Symbol.__init__(self, name) class BaseType(NamedType): + """ Built in type """ def __init__(self, name): super().__init__(name) @@ -87,6 +64,7 @@ class FunctionType(Type): + """ Function blueprint, defines argument types and return type """ def __init__(self, parametertypes, returntype): self.parametertypes = parametertypes self.returntype = returntype @@ -99,7 +77,7 @@ class PointerType(Type): """ A type that points to data of some other type """ def __init__(self, ptype): - assert isinstance(ptype, Type) or isinstance(ptype, Designator) + assert isinstance(ptype, Type) or isinstance(ptype, Expression) self.ptype = ptype def __repr__(self): @@ -108,17 +86,17 @@ class StructField: def __init__(self, name, typ): + assert type(name) is str self.name = name self.typ = typ self.offset = 0 class StructureType(Type): + """ Struct type consisting of several named members """ def __init__(self, mems): self.mems = mems - for mem in mems: - assert type(mem) is StructField - assert type(mem.name) is str + assert all(type(mem) is StructField for mem in mems) def hasField(self, name): for mem in self.mems: @@ -168,7 +146,6 @@ def __init__(self, name, typ): super().__init__(name) self.typ = typ - self.ival = None self.isLocal = False self.isReadOnly = False self.isParameter = False @@ -227,7 +204,8 @@ return 'TYPECAST {}'.format(self.to_type) -class FieldRef(Expression): +class Member(Expression): + """ Field reference of some object, can also be package selection """ def __init__(self, base, field, loc): super().__init__(loc) assert isinstance(base, Expression) @@ -236,10 +214,11 @@ self.field = field def __repr__(self): - return 'FIELD {}.{}'.format(self.base, self.field) + return 'MEMBER {}.{}'.format(self.base, self.field) class Unop(Expression): + """ Operation on one operand """ def __init__(self, op, a, loc): super().__init__(loc) assert isinstance(a, Expression) @@ -252,6 +231,7 @@ class Binop(Expression): + """ Expression taking two operands and one operator """ def __init__(self, a, op, b, loc): super().__init__(loc) assert isinstance(a, Expression), type(a) @@ -265,16 +245,19 @@ return 'BINOP {}'.format(self.op) -class VariableUse(Expression): +class Identifier(Expression): + """ Reference to some identifier, can be anything from package, variable + function or type, any named thing! """ def __init__(self, target, loc): super().__init__(loc) self.target = target def __repr__(self): - return 'VAR USE {}'.format(self.target) + return 'ID {}'.format(self.target) class Literal(Expression): + """ Constant value or string """ def __init__(self, val, loc): super().__init__(loc) self.val = val @@ -284,6 +267,7 @@ class FunctionCall(Expression): + """ Call to a some function """ def __init__(self, proc, args, loc): super().__init__(loc) self.proc = proc @@ -295,11 +279,22 @@ # Statements class Statement(Node): + """ Base class of all statements """ def __init__(self, loc): self.loc = loc +class EmptyStatement(Statement): + """ Empty statement which does nothing! """ + def __init__(self): + super().__init__(None) + + def __repr__(self): + return 'NOP' + + class CompoundStatement(Statement): + """ Statement consisting of a sequence of other statements """ def __init__(self, statements): super().__init__(None) self.statements = statements @@ -322,8 +317,8 @@ class Assignment(Statement): def __init__(self, lval, rval, loc): super().__init__(loc) - assert isinstance(lval, Node) - assert isinstance(rval, Node) + assert isinstance(lval, Expression) + assert isinstance(rval, Expression) self.lval = lval self.rval = rval diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/builder.py --- a/python/ppci/c3/builder.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/builder.py Mon Dec 09 19:00:21 2013 +0100 @@ -1,8 +1,11 @@ import logging +from .lexer import Lexer from .parser import Parser from .analyse import TypeChecker, Analyzer from .codegenerator import CodeGenerator from .analyse import AddScope +from .scope import createTopScope +from .visitor import AstPrinter class Builder: @@ -13,53 +16,55 @@ def __init__(self, diag, target): self.logger = logging.getLogger('c3') self.diag = diag + self.lexer = Lexer(diag) self.parser = Parser(diag) self.tc = TypeChecker(diag) self.al = Analyzer(diag) self.cg = CodeGenerator() - - def checkSource(self, srcs, imps=[]): - """ Performs syntax and type check. """ - iter(srcs) - iter(imps) - - def doParse(srcs): - for src in srcs: - pkg = self.parser.parseSource(src) - if pkg: - yield pkg - else: - self.ok = False - s_pkgs = set(doParse(srcs)) - i_pkgs = set(doParse(imps)) - all_pkgs = s_pkgs | i_pkgs - # Fix scopes: - - def doF(f, pkgs): - for pkg in pkgs: - if f(pkg): - yield pkg - else: - self.ok = False - all_pkgs = set(doF(AddScope(self.diag).addScope, all_pkgs)) - # TODO: fix error handling better - - def doA(pkgs): - packages = {pkg.name: pkg for pkg in pkgs} - for pkg in pkgs: - if self.al.analyzePackage(pkg, packages): - yield pkg - else: - self.ok = False - all_pkgs = set(doA(all_pkgs)) - all_pkgs = set(doF(self.tc.checkPackage, all_pkgs)) - return all_pkgs & s_pkgs + self.topScope = createTopScope(target) # Scope with built in types def build(self, srcs, imps=[]): """ Create IR-code from sources """ self.logger.info('Building {} source files'.format(len(srcs))) + iter(srcs) # Check if srcs are iterable + iter(imps) self.ok = True - for pkg in self.checkSource(srcs, imps): - # Only return ircode when everything is OK - if self.ok: - yield self.cg.gencode(pkg) + self.pkgs = {} + + def doParse(src): + tokens = self.lexer.lex(src) + return self.parser.parseSource(tokens) + s_pkgs = set(map(doParse, srcs)) + i_pkgs = set(map(doParse, imps)) + all_pkgs = s_pkgs | i_pkgs + if not all(all_pkgs): + self.ok = False + return + + #for pkg in all_pkgs: + # AstPrinter().printAst(pkg) + + packages = {pkg.name: pkg for pkg in all_pkgs} + self.pkgs = packages + # Fix scopes: + for pkg in all_pkgs: + AddScope(self.diag, self.topScope).addScope(pkg) + if not all(pkg.ok for pkg in all_pkgs): + self.ok = False + return + + for pkg in all_pkgs: + self.al.analyzePackage(pkg, packages) + if not all(pkg.ok for pkg in all_pkgs): + self.ok = False + return + + for pkg in all_pkgs: + self.tc.checkPackage(pkg) + if not all(pkg.ok for pkg in all_pkgs): + self.ok = False + return + + # Only return ircode when everything is OK + for pkg in all_pkgs & s_pkgs: + yield self.cg.gencode(pkg) diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/codegenerator.py --- a/python/ppci/c3/codegenerator.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/codegenerator.py Mon Dec 09 19:00:21 2013 +0100 @@ -1,7 +1,6 @@ import logging from .. import ir from . import astnodes -from .scope import boolType, intType from ppci import CompilerError from .analyse import theType @@ -43,7 +42,6 @@ # TODO: handle arguments f = self.funcMap[fn] f.return_value = self.newTemp() - # TODO reserve room for stack, this can be done at later point? self.setFunction(f) l2 = self.newBlock() self.emit(ir.Jump(l2)) @@ -77,6 +75,8 @@ if type(code) is astnodes.CompoundStatement: for s in code.statements: self.genCode(s) + elif type(code) is astnodes.EmptyStatement: + pass elif type(code) is astnodes.Assignment: rval = self.genExprCode(code.rval) lval = self.genExprCode(code.lval) @@ -92,12 +92,10 @@ self.genCode(code.truestatement) self.emit(ir.Jump(te)) self.setBlock(bbfalse) - if code.falsestatement: - self.genCode(code.falsestatement) + self.genCode(code.falsestatement) self.emit(ir.Jump(te)) self.setBlock(te) elif type(code) is astnodes.ReturnStatement: - assert code.expr re = self.genExprCode(code.expr) self.emit(ir.Move(self.fn.return_value, re)) self.emit(ir.Jump(self.fn.epiloog)) @@ -119,7 +117,6 @@ def genCondCode(self, expr, bbtrue, bbfalse): # Implement sequential logical operators - assert expr.typ == boolType if type(expr) is astnodes.Binop: if expr.op == 'or': l2 = self.newBlock() @@ -180,7 +177,7 @@ ar = self.genExprCode(expr.a) tt = theType(expr.to_type) if isinstance(tt, astnodes.PointerType): - if expr.a.typ is intType: + if expr.a.typ is expr.scope['int']: return ar elif isinstance(expr.a.typ, astnodes.PointerType): return ar diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/lexer.py --- a/python/ppci/c3/lexer.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/lexer.py Mon Dec 09 19:00:21 2013 +0100 @@ -19,6 +19,9 @@ def __init__(self, diag): self.diag = diag + def lex(self, source): + return self.tokenize(source) + def tokenize(self, input_file): """ Tokenizer, generates an iterator that diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/parser.py --- a/python/ppci/c3/parser.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/parser.py Mon Dec 09 19:00:21 2013 +0100 @@ -1,15 +1,16 @@ import logging from ppci import CompilerError -from .lexer import Lexer -from .astnodes import FieldRef, Literal, TypeCast, Unop, Binop +from .astnodes import Member, Literal, TypeCast, Unop, Binop from .astnodes import Assignment, ExpressionStatement, CompoundStatement from .astnodes import ReturnStatement, WhileStatement, IfStatement from .astnodes import FunctionType, Function, FormalParameter from .astnodes import StructureType, DefinedType, PointerType from .astnodes import Constant, Variable from .astnodes import StructField, Deref -from .astnodes import Package, ImportDesignator -from .astnodes import Designator, VariableUse, FunctionCall +from .astnodes import Package +from .astnodes import Identifier +from .astnodes import FunctionCall +from .astnodes import EmptyStatement class Parser: @@ -17,13 +18,14 @@ def __init__(self, diag): self.logger = logging.getLogger('c3') self.diag = diag - self.lexer = Lexer(diag) - def parseSource(self, source): + def parseSource(self, tokens): self.logger.info('Parsing source') - self.initLex(source) + self.tokens = tokens + self.token = self.tokens.__next__() try: self.parsePackage() + self.mod.ok = True # Valid until proven wrong :) return self.mod except CompilerError as e: self.diag.addDiag(e) @@ -58,10 +60,6 @@ self.token = self.tokens.__next__() return t - def initLex(self, source): - self.tokens = self.lexer.tokenize(source) - self.token = self.tokens.__next__() - def addDeclaration(self, decl): self.currentPart.declarations.append(decl) @@ -86,6 +84,7 @@ self.parseFunctionDef() elif self.Peak == 'var': self.parseVarDef() + # TODO handle variable initialization elif self.Peak == 'const': self.parseConstDef() elif self.Peak == 'type': @@ -98,11 +97,13 @@ def parseDesignator(self): """ A designator designates an object with a name. """ name = self.Consume('ID') - if self.hasConsumed(':'): - name2 = self.Consume('ID') - return ImportDesignator(name.val, name2.val, name.loc) - else: - return Designator(name.val, name.loc) + return Identifier(name.val, name.loc) + + def parseIdSequence(self): + ids = [self.Consume('ID')] + while self.hasConsumed(','): + ids.append(self.Consume('ID')) + return ids # Type system def parseTypeSpec(self): @@ -113,16 +114,16 @@ mems = [] while self.Peak != '}': mem_t = self.parseTypeSpec() - mem_n = self.Consume('ID').val - mems.append(StructField(mem_n, mem_t)) - while self.hasConsumed(','): - mem_n = self.Consume('ID').val - mems.append(StructField(mem_n, mem_t)) + for i in self.parseIdSequence(): + mems.append(StructField(i.val, mem_t)) self.Consume(';') self.Consume('}') theT = StructureType(mems) + elif self.Peak == 'enum': + # TODO) + raise NotImplementedError() else: - theT = self.parseDesignator() + theT = self.PostFixExpression() # Check for pointer suffix: while self.hasConsumed('*'): theT = PointerType(theT) @@ -140,35 +141,26 @@ def parseVarDef(self): self.Consume('var') t = self.parseTypeSpec() - - def parseVar(): - name = self.Consume('ID') + for name in self.parseIdSequence(): v = Variable(name.val, t) v.loc = name.loc - if self.hasConsumed('='): - v.ival = self.Expression() self.addDeclaration(v) - parseVar() - while self.hasConsumed(','): - parseVar() self.Consume(';') + return EmptyStatement() def parseConstDef(self): self.Consume('const') t = self.parseTypeSpec() - - def parseConst(): + while True: name = self.Consume('ID') self.Consume('=') val = self.Expression() c = Constant(name.val, t, val) c.loc = name.loc - parseConst() - while self.hasConsumed(','): - parseConst() + if not self.hasConsumed(','): + break self.Consume(';') - # Procedures def parseFunctionDef(self): loc = self.Consume('function').loc returntype = self.parseTypeSpec() @@ -180,34 +172,28 @@ self.Consume('(') parameters = [] if not self.hasConsumed(')'): - def parseParameter(): + while True: typ = self.parseTypeSpec() name = self.Consume('ID') param = FormalParameter(name.val, typ) param.loc = name.loc self.addDeclaration(param) parameters.append(param) - parseParameter() - while self.hasConsumed(','): - parseParameter() + if not self.hasConsumed(','): + break self.Consume(')') paramtypes = [p.typ for p in parameters] f.typ = FunctionType(paramtypes, returntype) f.body = self.parseCompoundStatement() self.currentPart = savePart - # Statements: - def parseIfStatement(self): loc = self.Consume('if').loc self.Consume('(') condition = self.Expression() self.Consume(')') - yes = self.parseCompoundStatement() - if self.hasConsumed('else'): - no = self.parseCompoundStatement() - else: - no = None + yes = self.Statement() + no = self.Statement() if self.hasConsumed('else') else EmptyStatement() return IfStatement(condition, yes, no, loc) def parseWhileStatement(self): @@ -215,7 +201,7 @@ self.Consume('(') condition = self.Expression() self.Consume(')') - statements = self.parseCompoundStatement() + statements = self.Statement() return WhileStatement(condition, statements, loc) def parseReturnStatement(self): @@ -231,10 +217,7 @@ self.Consume('{') statements = [] while not self.hasConsumed('}'): - s = self.Statement() - if s is None: - continue - statements.append(s) + statements.append(self.Statement()) return CompoundStatement(statements) def Statement(self): @@ -246,23 +229,20 @@ elif self.Peak == '{': return self.parseCompoundStatement() elif self.hasConsumed(';'): - pass + return EmptyStatement() elif self.Peak == 'var': - self.parseVarDef() + return self.parseVarDef() elif self.Peak == 'return': return self.parseReturnStatement() else: - return self.AssignmentOrCall() - - def AssignmentOrCall(self): - x = self.UnaryExpression() - if self.Peak == '=': - # We enter assignment mode here. - loc = self.Consume('=').loc - rhs = self.Expression() - return Assignment(x, rhs, loc) - else: - return ExpressionStatement(x, x.loc) + x = self.UnaryExpression() + if self.Peak == '=': + # We enter assignment mode here. + loc = self.Consume('=').loc + rhs = self.Expression() + return Assignment(x, rhs, loc) + else: + return ExpressionStatement(x, x.loc) # Expression section: # We not implement these C constructs: @@ -321,7 +301,7 @@ def BitwiseOr(self): a = self.BitwiseAnd() - while self.Peak in ['|']: + while self.Peak == '|': op = self.Consume(self.Peak) b = self.BitwiseAnd() a = Binop(a, op.typ, b, op.loc) @@ -329,7 +309,7 @@ def BitwiseAnd(self): a = self.CastExpression() - while self.Peak in ['&']: + while self.Peak == '&': op = self.Consume(self.Peak) b = self.CastExpression() a = Binop(a, op.typ, b, op.loc) @@ -383,12 +363,10 @@ elif self.hasConsumed('->'): field = self.Consume('ID') pfe = Deref(pfe, pfe.loc) - pfe = FieldRef(pfe, field.val, field.loc) + pfe = Member(pfe, field.val, field.loc) elif self.hasConsumed('.'): field = self.Consume('ID') - pfe = FieldRef(pfe, field.val, field.loc) - else: - raise Exception() + pfe = Member(pfe, field.val, field.loc) return pfe def PrimaryExpression(self): @@ -409,6 +387,5 @@ val = self.Consume('false') return Literal(False, val.loc) elif self.Peak == 'ID': - d = self.parseDesignator() - return VariableUse(d, d.loc) + return self.parseDesignator() self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak)) diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/scope.py --- a/python/ppci/c3/scope.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/scope.py Mon Dec 09 19:00:21 2013 +0100 @@ -1,8 +1,9 @@ -from . import astnodes +from .astnodes import Constant, Variable, Function, BaseType class Scope: - """ A scope contains all symbols in a scope """ + """ A scope contains all symbols in a scope. It also has a parent scope, + when looking for a symbol, also the parent scopes are checked. """ def __init__(self, parent=None): self.symbols = {} self.parent = parent @@ -18,53 +19,58 @@ @property def Constants(self): - return [s for s in self.Syms if type(s) is astnodes.Constant] + return [s for s in self.Syms if type(s) is Constant] @property def Variables(self): - return [s for s in self.Syms if isinstance(s, astnodes.Variable)] + return [s for s in self.Syms if isinstance(s, Variable)] @property def Functions(self): - return [s for s in self.Syms if type(s) is astnodes.Function] + return [s for s in self.Syms if type(s) is Function] def getSymbol(self, name): if name in self.symbols: return self.symbols[name] # Look for symbol: - if self.parent: + elif self.parent: return self.parent.getSymbol(name) - raise CompilerException("Symbol {0} not found".format(name), name.loc) + else: + raise KeyError(name) + + def __getitem__(self, key): + return self.getSymbol(key) def hasSymbol(self, name): if name in self.symbols: return True - if self.parent: + elif self.parent: return self.parent.hasSymbol(name) - return False + else: + return False + + def __contains__(self, name): + return self.hasSymbol(name) def addSymbol(self, sym): + assert sym.name not in self.symbols self.symbols[sym.name] = sym def __repr__(self): return 'Scope with {} symbols'.format(len(self.symbols)) -def createBuiltins(scope): +def createTopScope(target): + scope = Scope() for tn in ['u64', 'u32', 'u16', 'u8']: - scope.addSymbol(astnodes.BaseType(tn)) - for t in [intType, doubleType, voidType, boolType, stringType, byteType]: - scope.addSymbol(t) - -# buildin types: -intType = astnodes.BaseType('int') -intType.bytesize = 4 -doubleType = astnodes.BaseType('double') -voidType = astnodes.BaseType('void') -boolType = astnodes.BaseType('bool') -stringType = astnodes.BaseType('string') -byteType = astnodes.BaseType('byte') - -# Create top level scope: -topScope = Scope() -createBuiltins(topScope) + scope.addSymbol(BaseType(tn)) + # buildin types: + intType = BaseType('int') + intType.bytesize = target.byte_sizes['int'] + scope.addSymbol(intType) + scope.addSymbol(BaseType('double')) + scope.addSymbol(BaseType('void')) + scope.addSymbol(BaseType('bool')) + scope.addSymbol(BaseType('string')) + scope.addSymbol(BaseType('byte')) + return scope diff -r 0615b5308710 -r b145f8e6050b python/ppci/c3/visitor.py --- a/python/ppci/c3/visitor.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/ppci/c3/visitor.py Mon Dec 09 19:00:21 2013 +0100 @@ -12,7 +12,7 @@ self.do(node) def do(self, node): - # Run visitor: + # Run pre function: if self.f_pre: self.f_pre(node) @@ -23,6 +23,7 @@ elif type(node) is Function: for s in node.declarations: self.do(s) + self.do(node.typ) self.do(node.body) elif type(node) is CompoundStatement: for s in node.statements: @@ -30,11 +31,11 @@ elif type(node) is IfStatement: self.do(node.condition) self.do(node.truestatement) - if node.falsestatement: - self.do(node.falsestatement) + self.do(node.falsestatement) elif type(node) is FunctionCall: for arg in node.args: self.do(arg) + self.do(node.proc) elif type(node) is Assignment: self.do(node.lval) self.do(node.rval) @@ -49,14 +50,26 @@ self.do(node.ex) elif type(node) is TypeCast: self.do(node.a) - elif type(node) is FieldRef: + elif type(node) is Member: self.do(node.base) elif type(node) is Deref: self.do(node.ptr) elif type(node) is Constant: self.do(node.value) - elif type(node) in [VariableUse, Variable, Literal, FunctionType, - DefinedType, FormalParameter, LocalVariable]: + elif type(node) is DefinedType: + self.do(node.typ) + elif isinstance(node, Variable): + self.do(node.typ) + elif type(node) is PointerType: + self.do(node.ptype) + elif type(node) is StructureType: + for m in node.mems: + self.do(m.typ) + elif type(node) is FunctionType: + for pt in node.parametertypes: + self.do(pt) + self.do(node.returntype) + elif type(node) in [Identifier, Literal, EmptyStatement]: # Those nodes do not have child nodes. pass elif type(node) is WhileStatement: diff -r 0615b5308710 -r b145f8e6050b python/target/basetarget.py --- a/python/target/basetarget.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/target/basetarget.py Mon Dec 09 19:00:21 2013 +0100 @@ -141,6 +141,7 @@ self.desc = desc self.registers = [] self.instructions = [] + self.byte_sizes = {'int':4} # For front end! def instruction(self, cls): """ Decorator function that registers an instruction to this target """ diff -r 0615b5308710 -r b145f8e6050b python/utils/hexfile.py --- a/python/utils/hexfile.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/utils/hexfile.py Mon Dec 09 19:00:21 2013 +0100 @@ -81,7 +81,7 @@ raise HexFileException('record type {0} not implemented'.format(typ)) def __repr__(self): - size = sum(len(r.data) for r in self.regions) + size = sum(r.Size for r in self.regions) return 'Hexfile containing {} bytes'.format(size) def dump(self): @@ -150,5 +150,9 @@ self.data = self.data + d @property + def Size(self): + return len(self.data) + + @property def EndAddress(self): return self.address + len(self.data) diff -r 0615b5308710 -r b145f8e6050b python/zcc.py --- a/python/zcc.py Fri Dec 06 13:50:38 2013 +0100 +++ b/python/zcc.py Mon Dec 09 19:00:21 2013 +0100 @@ -64,7 +64,6 @@ for ircode in c3b.build(srcs, imps): if not ircode: return - # Optimization passes, TODO if dumpir: diff -r 0615b5308710 -r b145f8e6050b test/runtests.sh --- a/test/runtests.sh Fri Dec 06 13:50:38 2013 +0100 +++ b/test/runtests.sh Mon Dec 09 19:00:21 2013 +0100 @@ -2,7 +2,8 @@ export PYTHONPATH=$PYTHONPATH:`pwd`/../python -if [ $1 == "loop" ]; then +if [ "$1" == "loop" ] +then DIR=.. while :; do python -m unittest diff -r 0615b5308710 -r b145f8e6050b test/testc3.py --- a/test/testc3.py Fri Dec 06 13:50:38 2013 +0100 +++ b/test/testc3.py Mon Dec 09 19:00:21 2013 +0100 @@ -45,23 +45,35 @@ self.builder = Builder(self.diag, SimpleTarget()) self.diag.clear() + def makeFileList(self, snippet): + """ Try to make a list with opened files """ + if type(snippet) is list: + l2 = [] + for s in snippet: + if type(s) is str: + l2.append(io.StringIO(s)) + else: + l2.append(s) + return l2 + else: + return [io.StringIO(snippet)] + def expectErrors(self, snippet, rows): """ Helper to test for expected errors on rows """ ircode = list(self.builder.build([io.StringIO(snippet)])) actualErrors = [err.row for err in self.diag.diags] if rows != actualErrors: - self.diag.printErrors(snippet) + self.diag.printErrors() self.assertSequenceEqual(rows, actualErrors) # self.assertFalse(all(ircode)) def expectOK(self, snippet): - if type(snippet) is list: - ircode = self.builder.build(snippet) - else: - ircode = self.builder.build([io.StringIO(snippet)]) - if not ircode: - self.diag.printErrors(snippet) + """ Expect a snippet to be OK """ + ircode = list(self.builder.build(self.makeFileList(snippet))) + if len(self.diag.diags) > 0: + self.diag.printErrors() self.assertTrue(all(ircode)) + self.assertEqual(0, len(self.diag.diags)) return ircode def testPackage(self): @@ -70,9 +82,9 @@ """ p2 = """module p2; import p1; - var A b; + var p1.A b; """ - self.expectOK([io.StringIO(s) for s in (p1, p2)]) + self.expectOK([p1, p2]) def testPackageMutual(self): p1 = """module p1; @@ -84,13 +96,13 @@ import p1; var p1.A a; """ - self.expectOK([io.StringIO(s) for s in (p1, p2)]) + self.expectOK([p1, p2]) def testPackageNotExists(self): p1 = """module p1; import p23; """ - self.expectOK([io.StringIO(p1)]) + self.expectErrors(p1, [0]) def testFunctArgs(self): snippet = """ @@ -177,7 +189,8 @@ var int a; function void t() { - var int i = 0; + var int i; + i = 0; while (i < 1054) { i = i + 3;