Mercurial > lcfOS
changeset 300:158068af716c
yafm
line wrap: on
line diff
--- a/doc/compiler.rst Sun Dec 01 18:37:23 2013 +0100 +++ b/doc/compiler.rst Tue Dec 03 18:00:22 2013 +0100 @@ -4,17 +4,11 @@ ======== This chapter describes the design of the compiler. - - -Overview --------- - The compiler consists a frontend, mid-end and back-end. The frontend deals with source file parsing and semantics checking. The mid-end performs optimizations. This is optional. The back-end generates machine code. The front-end produces intermediate code. This is a simple representation of the source. The back-end -can accept this kind of representation. This way the compiler is portable and -a front end can be constructed without having to do the rest. +can accept this kind of representation. .. graphviz:: @@ -38,6 +32,7 @@ IR-code ------- + The intermediate representation (IR) of a program de-couples the front end from the backend of the compiler. @@ -53,6 +48,7 @@ .. graphviz:: digraph c3 { + rankdir="LR" 1 [label="source text"] 10 [label="lexer" ] 20 [label="parser" ]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/blink.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,30 @@ +/* This file blinks a LED on the STM32F4 discovery board. + +the board has 4 leds on PD12, PD13, PD14 and PD15 + +*/ + +module blink; + +import stm32f4xx; + +// Functions: +function void main() +{ + // Memory mapped control registers: + var GPIO_Type GPIOD; + GPIOD = cast<GPIO_Type>(0x40020C00); + var RCC_Type RCC; + RCC = cast<RCC_Type>(0x40023800); + + // Enable the clock to port D: + RCC->AHB1ENR = RCC->AHB1ENR | 0x8; + + + // PD13 == output (01) + GPIOD->MODER = (1 << 26); + GPIOD->ODR = (1 << 13); + + while(true) {} +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/burn.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,39 @@ +/* + +This file blinks a LED on the STM32F4 discovery board. + +the board has 4 leds on PD12, PD13, PD14 and PD15 + +*/ + +module burn; + +import stm32f4xx; + +/* +function void init() +{ +} +*/ + +function void main() +{ + //init(); + var RCC_Type RCC; + RCC = cast<RCC_Type>(0x40023800); + + // Enable the clock to port D: + RCC->AHB1ENR = RCC->AHB1ENR | (1 << 3); + // Memory mapped control registers: + var GPIO_Type GPIOD; + GPIOD = cast<GPIO_Type>(0x40020C00); + + var int pin; + pin = 15; + // PD13 == output (01) + GPIOD->MODER = (1 << (pin << 1)); + GPIOD->ODR = (1 << pin); + + while(true) {} +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/burn.c3proj Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,6 @@ + +[Burn] +srcs = burn2.c3 +imps = stm32f4xx.c3 +target = arm +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/burn2.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,59 @@ +/* + +This file blinks a LED on the STM32F4 discovery board. + +the board has 4 leds on PD12, PD13, PD14 and PD15 + +*/ + +module burn2; + +import stm32f4xx; + +function int add(int a, int b) +{ + return a + b; +} + +function void init(int pin) +{ + if (pin < 12) + { + return; + } + + if (pin > 15) + { + return; + } + + var stm32f4xx:RCC_Type RCC; + RCC = cast<stm32f4xx:RCC_Type>(0x40023800); + + // Enable the clock to port D: + RCC->AHB1ENR = RCC->AHB1ENR | (1 << 3); + // Memory mapped control registers: + var stm32f4xx:GPIO_Type GPIOD; + GPIOD = cast<stm32f4xx:GPIO_Type>(0x40020C00); + + // PD13 == output (01) + GPIOD->MODER = (1 << (pin << 1)); + GPIOD->ODR = (1 << pin); +} + + +function void main() +{ + // Vary between 12 and 15: + init(13); + + var int a; + a = 0 + while (a < 1000) + { + a = add(a, 1); + } + + while(true) {} +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/cast.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,18 @@ + +/* + Demo of how to type cast +*/ + +module castdemo; + +function int testcast() +{ + var int a; + var int* b; + a = 3; + + b = cast<int*>(a); + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/comments.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,14 @@ + +/* + block + comment +*/ + +// fjd jjd- +module comments; // hjfkds + +function int tst() +{ + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/coro.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,91 @@ +module coro; + +/* + Some co-routines doing some work. + + This example demonstrates how to write co-operating + routines in a nice way. +*/ + +int regbank[10]; + +function i2c_write(int address, int d) +{ + regbank[2] = address; + regbank[0] |= 0x1; // Issue go command + while (regbank[1] != 0) + { + yield; + } + + // Wait while busy: + while (regbank[1] != 11) + { + yield; + } +} + +function int i2c_read(int address) +{ + regbank[2] = address; + regbank[0] |= 0x1; // Issue go command + while (regbank[1] != 0) + { + yield; + } + + // Wait while busy: + while (regbank[1] != 11) + { + yield; + } +} + +function void eeprom_set(int address, int v) +{ + i2c_write(address, v); + i2c_read(address); +} + +function int calcX(int y) +{ + var int x; + x = 2; + x = x + 2 + 9 * y; + return x; +} + +var int counter = 0; + +task task1() +{ + start task3; + var int a = 200; + while (a > 0) + { + yield; + } +} + +task task2() +{ + while(true) + { + yield; + } +} + +task task3() +{ + eeprom_set(99, 1); + yield; +} + +task main() +{ + start task1; + start task2; + await task1; + await task2; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/functions.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,27 @@ + +/* + Demo of function usage +*/ + +module functiondemo; + +function void main() +{ + var int a, b, c; + a = 3; + b = a; + a =3; + b = fib(a + 9); + sum(a, b); +} + +function int fib(int x) +{ + return fib(x - 1) * x; +} + +function int sum(int a, int b) +{ + return a + b; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/stm32f4xx.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,31 @@ + +module stm32f4xx; + +type struct { + int MODER; + int OTYPER; + int OSPEEDR; + int PUPDR; + int IDR; + int ODR; +}* GPIO_Type; + +type struct { + int CR; + int PLLCFGR; + int CFGR; + int CIR; + int AHB1RSTR; + int AHB2RSTR; + int AHB3RSTR; + int reserved0; + int APB1RSTR; + int APB2RSTR; + int reserved1a, reserved1b; + int AHB1ENR; + int AHB2ENR; + int AHB3ENR; + int reserved2; + int APB1ENR, APB2ENR; +}* RCC_Type; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/c3/types.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,40 @@ + +/* + Demo of how to define types +*/ + +module typedemo; + +type int A; +type int B; +type struct { + int x, y; + A z; +} C; +type struct { + C x; + B y; + int z; +} D; + +type D* E; + +function int testcast() +{ + var A a; + var B b; + a = 3; + b = a; + var C c; + c.x = a; + c.z = c.y; + var D d; + var E e; + var D* e2; + e = &d; + e2 = e; + e2->x.x = 22; + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/arch_arm.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,10 @@ +module arch; + +function void init() +{ +} + +function void halt() +{ +} +
--- a/kernel/make.py Sun Dec 01 18:37:23 2013 +0100 +++ b/kernel/make.py Tue Dec 03 18:00:22 2013 +0100 @@ -5,7 +5,7 @@ sys.path.insert(0, os.path.join('..', 'python')) import zcc -arglist = ['memory.c3', 'kernel.c3', 'syscall.c3', 'process.c3', 'schedule.c3'] +arglist = ['memory.c3', 'kernel.c3', 'syscall.c3', 'process.c3', 'schedule.c3', 'arch_arm.c3'] arglist += ['--target', 'arm'] arglist += ['--dumpasm'] arglist += ['--log', 'debug']
--- a/kernel/process.c3 Sun Dec 01 18:37:23 2013 +0100 +++ b/kernel/process.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -1,4 +1,5 @@ module process; + import memory; import kernel; @@ -12,13 +13,13 @@ // List<process_t> procs; // init is the root of all processes: -var process_t* init = 0; +var process_t* init_pid = 0; var int next_pid = 0; function void init() { next_pid = 0; - init = Create(); + init_pid = Create(); } /* @@ -26,19 +27,19 @@ */ function process_t* Create() { - process_t* p = memory.Alloc(sizeof(process_t)); + // process_t* p = memory.Alloc(sizeof(process_t)); p->id = next_pid; - next_pid++; + next_pid = next_pid + 1; return p; } -public func void Kill(process_t* p) +function void Kill(process_t* p) { // clean memory } -public process_t* byId(int id) +function process_t* byId(int id) { // Perform lookup return 0;
--- a/kernel/syscall.c3 Sun Dec 01 18:37:23 2013 +0100 +++ b/kernel/syscall.c3 Tue Dec 03 18:00:22 2013 +0100 @@ -4,14 +4,18 @@ This module handles all the system calls from user space. */ +/* enum { SendMsg = 1, ReceiveMsg = 2, } syscall_t; +*/ + +type int syscall_t; // System call handlers. System calls are made from user space. -func void handle_system_call(int callId, int a, int b, int c, int d) +function void handle_system_call(int callId, int a, int b, int c, int d) { // Main entry, check what to do here switch(callId) @@ -25,7 +29,7 @@ } proc.setMessage(); - scheduler.current.setState(Sleep); + // scheduler.current.setState(Sleep); break; case ReceiveMsg: break;
--- a/python/c3/__init__.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -""" This is the C3 language front end. """ - - -from .parser import Parser -from .lexer import Lexer -from .analyse import Analyzer, TypeChecker -from .codegenerator import CodeGenerator -from .visitor import Visitor, AstPrinter -from .builder import Builder
--- a/python/c3/analyse.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -import logging -from .visitor import Visitor -from .astnodes import * -from .scope import * - - -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.diag.error(msg, loc) - - def visit(self, pkg, pre, post): - self.visitor.visit(pkg, pre, post) - - -class AddScope(C3Pass): - """ 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] - 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): - return self.scopeStack[-1] - - def addSymbol(self, sym): - if self.CurrentScope.hasSymbol(sym.name): - self.error('Redefinition of {0}'.format(sym.name), sym.loc) - else: - self.CurrentScope.addSymbol(sym) - - def enterScope(self, sym): - # Distribute the scope: - sym.scope = self.CurrentScope - - # Add symbols to current scope: - if isinstance(sym, Symbol) or isinstance(sym, DefinedType): - self.addSymbol(sym) - - # Create subscope: - if type(sym) in [Package, Function]: - 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]: - self.scopeStack.pop(-1) - - -class Analyzer(C3Pass): - """ - Context handling is done here. - Scope is attached to the correct modules. - This class checks names and references. - """ - - def analyzePackage(self, pkg, packageDict): - self.ok = True - # Prepare top level scope and set scope to all objects: - - self.logger.info('Resolving imports for package {}'.format(pkg.name)) - # Handle imports: - for i in pkg.imports: - if i not in packageDict: - self.error('Cannot import {}'.format(i)) - continue - ip = packageDict[i] - pkg.scope.addSymbol(ip) - FixRefs(self.diag).fixRefs(pkg) - return self.ok - - -class FixRefs(C3Pass): - def fixRefs(self, pkg): - 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: - if hasattr(s, 'addRef'): - # TODO: make this nicer - s.addRef(None) - return s - else: - self.error('Cannot resolve name {0}'.format(d.tname), d.loc) - - def resolveImportDesignator(self, d, scope): - assert isinstance(d, ImportDesignator), type(d) - assert type(scope) is Scope - if scope.hasSymbol(d.tname): - s = scope.getSymbol(d.tname) - if hasattr(s, 'addRef'): - # TODO: make this nicer - s.addRef(None) - return s - else: - 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) in [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 - 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) - - -# Type checking: - -def theType(t): - """ Recurse until a 'real' type is found """ - if type(t) is DefinedType: - return theType(t.typ) - return t - - -def equalTypes(a, b): - """ Compare types a and b for structural equavalence. """ - # Recurse into named types: - a, b = theType(a), theType(b) - - if type(a) is type(b): - if type(a) is BaseType: - return a.name == b.name - elif type(a) is PointerType: - return equalTypes(a.ptype, b.ptype) - elif type(a) is StructureType: - if len(a.mems) != len(b.mems): - return False - 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))) - return False - - -def canCast(fromT, toT): - fromT = theType(fromT) - toT = theType(toT) - if isinstance(fromT, PointerType) and isinstance(toT, PointerType): - return True - elif fromT is intType and isinstance(toT, PointerType): - return True - return False - - -def expectRval(s): - # TODO: solve this better - s.expect_rvalue = True - - -class TypeChecker(C3Pass): - def checkPackage(self, pkg): - self.ok = True - self.visit(pkg, None, self.check2) - return self.ok - - 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) - self.error(msg, sym.condition.loc) - elif type(sym) is Assignment: - l, r = sym.lval, sym.rval - if not equalTypes(l.typ, r.typ): - msg = 'Cannot assign {} to {}'.format(r.typ, l.typ) - self.error(msg, sym.loc) - if not l.lvalue: - self.error('No valid lvalue {}'.format(l), l.loc) - #if sym.rval.lvalue: - # self.error('Right hand side must be an rvalue', sym.rval.loc) - expectRval(sym.rval) - elif type(sym) is ReturnStatement: - pass - elif type(sym) is FunctionCall: - # Check arguments: - ngiv = len(sym.args) - ptypes = sym.proc.typ.parametertypes - nreq = len(ptypes) - if ngiv != nreq: - self.error('Function {2}: {0} arguments required, {1} given'.format(nreq, ngiv, sym.proc.name), sym.loc) - else: - for a, at in zip(sym.args, ptypes): - expectRval(a) - if not equalTypes(a.typ, at): - self.error('Got {0}, expected {1}'.format(a.typ, at), a.loc) - # determine return type: - sym.typ = sym.proc.typ.returntype - elif type(sym) is VariableUse: - sym.lvalue = True - if isinstance(sym.target, Variable): - sym.typ = sym.target.typ - else: - print('warning {} has no target, defaulting to int'.format(sym)) - sym.typ = intType - 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 - else: - raise Exception('Unknown literal type'.format(sym.val)) - elif type(sym) is Unop: - if sym.op == '&': - sym.typ = PointerType(sym.a.typ) - sym.lvalue = False - else: - raise Exception('Unknown unop {0}'.format(sym.op)) - elif type(sym) is Deref: - # pointer deref - sym.lvalue = True - # check if the to be dereferenced variable is a pointer type: - 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: - basetype = sym.base.typ - sym.lvalue = sym.base.lvalue - basetype = theType(basetype) - if type(basetype) is StructureType: - if basetype.hasField(sym.field): - sym.typ = basetype.fieldType(sym.field) - else: - self.error('{} does not contain field {}'.format(basetype, sym.field), sym.loc) - sym.typ = intType - else: - self.error('Cannot select field {} of non-structure type {}'.format(sym.field, basetype), sym.loc) - sym.typ = intType - elif type(sym) is Binop: - 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): - sym.typ = sym.a.typ - else: - self.error('Can only add integers', sym.loc) - sym.typ = intType - else: - # assume void here? TODO: throw exception! - sym.typ = 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 - 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) - else: - raise Exception('Unknown binop {0}'.format(sym.op)) - elif isinstance(sym, Variable): - # check initial value type: - # TODO - pass - elif type(sym) is TypeCast: - 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 - 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]: - pass - else: - raise NotImplementedError('Unknown type check {0}'.format(sym))
--- a/python/c3/astnodes.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,356 +0,0 @@ -""" -AST (abstract syntax tree) nodes for the c3 language. -The tree is build by the parser. -Then it is checked -Finally code is generated from it. -""" - -from ppci import SourceLocation - - -class Node: - pass - - -# Modules -class Package(Node): - def __init__(self, name, loc): - self.name = name - self.loc = loc - self.declarations = [] - self.imports = [] - - def __repr__(self): - 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): - pass - - -class BaseType(Type): - def __init__(self, name): - self.name = name - - def __repr__(self): - return '{}'.format(self.name) - - -class FunctionType(Type): - def __init__(self, parametertypes, returntype): - self.parametertypes = parametertypes - self.returntype = returntype - - def __repr__(self): - params = ', '.join([str(v) for v in self.parametertypes]) - return '{1} f({0})'.format(params, self.returntype) - - -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) - self.ptype = ptype - - def __repr__(self): - return '({}*)'.format(self.ptype) - - -class StructField: - def __init__(self, name, typ): - self.name = name - self.typ = typ - self.offset = 0 - - -class StructureType(Type): - def __init__(self, mems): - self.mems = mems - for mem in mems: - assert type(mem) is StructField - assert type(mem.name) is str - - def hasField(self, name): - for mem in self.mems: - if name == mem.name: - return True - return False - - def fieldType(self, name): - return self.findField(name).typ - - def fieldOffset(self, name): - return self.findField(name).offset - - def findField(self, name): - for mem in self.mems: - if name == mem.name: - return mem - raise KeyError(name) - - def __repr__(self): - return 'STRUCT' - - -class DefinedType(Type): - """ A named type indicating another type """ - def __init__(self, name, typ, loc): - assert isinstance(name, str) - self.name = name - self.typ = typ - self.loc = loc - - def __repr__(self): - return 'Named type {0} of type {1}'.format(self.name, self.typ) - - -# Variables, parameters, local variables, constants: -class Symbol(Node): - 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, 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): - super().__init__(name) - self.typ = typ - self.ival = None - self.isLocal = False - self.isReadOnly = False - self.isParameter = False - - def __repr__(self): - return 'Var {} [{}]'.format(self.name, self.typ) - - -class LocalVariable(Variable): - def __init__(self, name, typ): - super().__init__(name, typ) - self.isLocal = True - - -class FormalParameter(Variable): - def __init__(self, name, typ): - super().__init__(name, typ) - self.isParameter = True - - -# Procedure types -class Function(Symbol): - """ Actual implementation of a function """ - def __init__(self, name, loc): - super().__init__(name) - self.loc = loc - self.declarations = [] - - def __repr__(self): - return 'Func {}'.format(self.name) - - -# Operations / Expressions: -class Expression(Node): - def __init__(self, loc): - self.loc = loc - - -class Deref(Expression): - def __init__(self, ptr, loc): - super().__init__(loc) - assert isinstance(ptr, Expression) - self.ptr = ptr - - def __repr__(self): - return 'DEREF {}'.format(self.ptr) - - -class TypeCast(Expression): - def __init__(self, to_type, x, loc): - super().__init__(loc) - self.to_type = to_type - self.a = x - - def __repr__(self): - return 'TYPECAST {}'.format(self.to_type) - - -class FieldRef(Expression): - def __init__(self, base, field, loc): - super().__init__(loc) - assert isinstance(base, Expression) - assert isinstance(field, str) - self.base = base - self.field = field - - def __repr__(self): - return 'FIELD {}.{}'.format(self.base, self.field) - - -class Unop(Expression): - def __init__(self, op, a, loc): - super().__init__(loc) - assert isinstance(a, Expression) - assert isinstance(op, str) - self.a = a - self.op = op - - def __repr__(self): - return 'UNOP {}'.format(self.op) - - -class Binop(Expression): - def __init__(self, a, op, b, loc): - super().__init__(loc) - assert isinstance(a, Expression), type(a) - assert isinstance(b, Expression) - assert isinstance(op, str) - self.a = a - self.b = b - self.op = op # Operation: '+', '-', '*', '/', 'mod' - - def __repr__(self): - return 'BINOP {}'.format(self.op) - - -class VariableUse(Expression): - def __init__(self, target, loc): - super().__init__(loc) - self.target = target - - def __repr__(self): - return 'VAR USE {}'.format(self.target) - - -class Literal(Expression): - def __init__(self, val, loc): - super().__init__(loc) - self.val = val - - def __repr__(self): - return 'LITERAL {}'.format(self.val) - - -class FunctionCall(Expression): - def __init__(self, proc, args, loc): - super().__init__(loc) - self.proc = proc - self.args = args - - def __repr__(self): - return 'CALL {0} '.format(self.proc) - - -# Statements -class Statement(Node): - def __init__(self, loc): - self.loc = loc - - -class CompoundStatement(Statement): - def __init__(self, statements): - super().__init__(None) - self.statements = statements - for s in self.statements: - assert isinstance(s, Statement) - - def __repr__(self): - return 'COMPOUND STATEMENT' - - -class ReturnStatement(Statement): - def __init__(self, expr, loc): - super().__init__(loc) - self.expr = expr - - def __repr__(self): - return 'RETURN STATEMENT' - - -class Assignment(Statement): - def __init__(self, lval, rval, loc): - super().__init__(loc) - assert isinstance(lval, Node) - assert isinstance(rval, Node) - self.lval = lval - self.rval = rval - - def __repr__(self): - return 'ASSIGNMENT' - - -class ExpressionStatement(Statement): - def __init__(self, ex, loc): - super().__init__(loc) - self.ex = ex - - def __repr__(self): - return 'Epression' - - -class IfStatement(Statement): - def __init__(self, condition, truestatement, falsestatement, loc): - super().__init__(loc) - self.condition = condition - self.truestatement = truestatement - self.falsestatement = falsestatement - - def __repr__(self): - return 'IF-statement' - - -class WhileStatement(Statement): - def __init__(self, condition, statement, loc): - super().__init__(loc) - self.condition = condition - self.statement = statement - - def __repr__(self): - return 'WHILE-statement'
--- a/python/c3/builder.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -import logging -import ppci -from . import Parser, TypeChecker, Analyzer, CodeGenerator -from .analyse import AddScope - - -class Builder: - """ - Generates IR-code from c3 source. - Reports errors to the diagnostics system - """ - def __init__(self, diag): - self.logger = logging.getLogger('c3') - self.diag = 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 - - def build(self, srcs, imps=[]): - """ Create IR-code from sources """ - 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)
--- a/python/c3/codegenerator.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ -import logging -import ir -from . import astnodes -from .scope import boolType, intType -from ppci import CompilerError -from .analyse import theType - - -class CodeGenerator(ir.Builder): - """ - Generates intermediate (IR) code from a package. The entry function is - 'genModule'. The main task of this part is to rewrite complex control - structures, such as while and for loops into simple conditional - jump statements. Also complex conditional statements are simplified. - Such as 'and' and 'or' statements are rewritten in conditional jumps. - And structured datatypes are rewritten. - """ - def __init__(self): - self.logger = logging.getLogger('c3cgen') - - def gencode(self, pkg): - self.prepare() - assert type(pkg) is astnodes.Package - self.logger.info('Generating ir-code for {}'.format(pkg.name)) - self.varMap = {} # Maps variables to storage locations. - self.funcMap = {} - self.m = ir.Module(pkg.name) - self.genModule(pkg) - return self.m - - # inner helpers: - def genModule(self, pkg): - # Take care of forward declarations: - for s in pkg.innerScope.Functions: - f = self.newFunction(s.name) - self.funcMap[s] = f - for v in pkg.innerScope.Variables: - self.varMap[v] = self.newTemp() - for s in pkg.innerScope.Functions: - self.genFunction(s) - - def genFunction(self, fn): - # 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)) - self.setBlock(l2) - # generate room for locals: - - for sym in fn.innerScope: - # TODO: handle parameters different - if sym.isParameter: - v = ir.Parameter(sym.name) - f.addParameter(v) - elif sym.isLocal: - v = ir.LocalVariable(sym.name) - f.addLocal(v) - else: - #v = self.newTemp() - raise NotImplementedError('{}'.format(sym)) - # TODO: make this ssa here?? - self.varMap[sym] = v - - self.genCode(fn.body) - # Set the default return value to zero: - # TBD: this may not be required? - self.emit(ir.Move(f.return_value, ir.Const(0))) - self.emit(ir.Jump(f.epiloog)) - self.setFunction(None) - - def genCode(self, code): - assert isinstance(code, astnodes.Statement) - self.setLoc(code.loc) - if type(code) is astnodes.CompoundStatement: - for s in code.statements: - self.genCode(s) - elif type(code) is astnodes.Assignment: - rval = self.genExprCode(code.rval) - lval = self.genExprCode(code.lval) - self.emit(ir.Move(lval, rval)) - elif type(code) is astnodes.ExpressionStatement: - self.emit(ir.Exp(self.genExprCode(code.ex))) - elif type(code) is astnodes.IfStatement: - bbtrue = self.newBlock() - bbfalse = self.newBlock() - te = self.newBlock() - self.genCondCode(code.condition, bbtrue, bbfalse) - self.setBlock(bbtrue) - self.genCode(code.truestatement) - self.emit(ir.Jump(te)) - self.setBlock(bbfalse) - if code.falsestatement: - self.genCode(code.falsestatement) - self.emit(ir.Jump(te)) - self.setBlock(te) - elif type(code) is astnodes.ReturnStatement: - if code.expr: - re = self.genExprCode(code.expr) - self.emit(ir.Move(self.fn.return_value, re)) - self.emit(ir.Jump(self.fn.epiloog)) - b = self.newBlock() - self.setBlock(b) - else: - self.builder.addIns(ir.Return()) - elif type(code) is astnodes.WhileStatement: - bbdo = self.newBlock() - bbtest = self.newBlock() - te = self.newBlock() - self.emit(ir.Jump(bbtest)) - self.setBlock(bbtest) - self.genCondCode(code.condition, bbdo, te) - self.setBlock(bbdo) - self.genCode(code.statement) - self.emit(ir.Jump(bbtest)) - self.setBlock(te) - else: - raise NotImplementedError('Unknown stmt {}'.format(code)) - - 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() - self.genCondCode(expr.a, bbtrue, l2) - self.setBlock(l2) - self.genCondCode(expr.b, bbtrue, bbfalse) - elif expr.op == 'and': - l2 = self.newBlock() - self.genCondCode(expr.a, l2, bbfalse) - self.setBlock(l2) - self.genCondCode(expr.b, bbtrue, bbfalse) - elif expr.op in ['==', '>', '<']: - ta = self.genExprCode(expr.a) - tb = self.genExprCode(expr.b) - self.emit(ir.CJump(ta, expr.op, tb, bbtrue, bbfalse)) - else: - raise NotImplementedError('Unknown condition {}'.format(expr)) - elif type(expr) is astnodes.Literal: - if expr.val: - self.emit(ir.Jump(bbtrue)) - else: - self.emit(ir.Jump(bbfalse)) - else: - raise NotImplementedError('Unknown cond {}'.format(expr)) - - def genExprCode(self, expr): - assert isinstance(expr, astnodes.Expression) - if type(expr) is astnodes.Binop and expr.op in ir.Binop.ops: - ra = self.genExprCode(expr.a) - rb = self.genExprCode(expr.b) - return ir.Binop(ra, expr.op, rb) - elif type(expr) is astnodes.Unop and expr.op == '&': - ra = self.genExprCode(expr.a) - assert type(ra) is ir.Mem - return ra.e - elif type(expr) is astnodes.VariableUse: - # This returns the dereferenced variable. - if expr.target.isParameter: - # TODO: now parameters are handled different. Not nice? - return self.varMap[expr.target] - else: - return ir.Mem(self.varMap[expr.target]) - elif type(expr) is astnodes.Deref: - # dereference pointer type: - addr = self.genExprCode(expr.ptr) - return ir.Mem(addr) - elif type(expr) is astnodes.FieldRef: - base = self.genExprCode(expr.base) - assert type(base) is ir.Mem, type(base) - base = base.e - bt = theType(expr.base.typ) - offset = ir.Const(bt.fieldOffset(expr.field)) - return ir.Mem(ir.Add(base, offset)) - elif type(expr) is astnodes.Literal: - return ir.Const(expr.val) - elif type(expr) is astnodes.TypeCast: - # TODO: improve this mess: - ar = self.genExprCode(expr.a) - tt = theType(expr.to_type) - if isinstance(tt, astnodes.PointerType): - if expr.a.typ is intType: - return ar - elif isinstance(expr.a.typ, astnodes.PointerType): - return ar - else: - raise Exception() - else: - raise NotImplementedError("not implemented") - elif type(expr) is astnodes.FunctionCall: - args = [self.genExprCode(e) for e in expr.args] - fn = self.funcMap[expr.proc] - return ir.Call(fn, args) - else: - raise NotImplementedError('Unknown expr {}'.format(expr))
--- a/python/c3/lexer.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -import collections -import re - -from ppci import CompilerError, SourceLocation, Token - -""" - Lexical analyzer part. Splits the input character stream into tokens. -""" - -keywords = ['and', 'or', 'not', 'true', 'false', - 'else', 'if', 'while', 'return', - 'function', 'var', 'type', 'const', - 'struct', 'cast', - 'import', 'module'] - - -class Lexer: - def __init__(self, diag): - self.diag = diag - - def tokenize(self, input_file): - """ - Tokenizer, generates an iterator that - returns tokens! - - Input is a file like object. - - This GREAT example was taken from python re doc page! - """ - filename = input_file.name if hasattr(input_file, 'name') else '' - s = input_file.read() - input_file.close() - self.diag.addSource(filename, s) - tok_spec = [ - ('REAL', r'\d+\.\d+'), - ('HEXNUMBER', r'0x[\da-fA-F]+'), - ('NUMBER', r'\d+'), - ('ID', r'[A-Za-z][A-Za-z\d_]*'), - ('NEWLINE', r'\n'), - ('SKIP', r'[ \t]'), - ('COMMENTS', r'//.*'), - ('LONGCOMMENTBEGIN', r'\/\*'), - ('LONGCOMMENTEND', r'\*\/'), - ('LEESTEKEN', r'==|->|<<|>>|[\.,=:;\-+*\[\]/\(\)]|>=|<=|<>|>|<|{|}|&|\^|\|'), - ('STRING', r"'.*?'") - ] - tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) - gettok = re.compile(tok_re).match - line = 1 - pos = line_start = 0 - mo = gettok(s) - incomment = False - while mo is not None: - typ = mo.lastgroup - val = mo.group(typ) - if typ == 'NEWLINE': - line_start = pos - line += 1 - elif typ == 'COMMENTS': - pass - elif typ == 'LONGCOMMENTBEGIN': - incomment = True - elif typ == 'LONGCOMMENTEND': - incomment = False - elif typ == 'SKIP': - pass - elif incomment: - pass # Wait until we are not in a comment section - else: - if typ == 'ID': - if val in keywords: - typ = val - elif typ == 'LEESTEKEN': - typ = val - elif typ == 'NUMBER': - val = int(val) - elif typ == 'HEXNUMBER': - val = int(val[2:], 16) - typ = 'NUMBER' - elif typ == 'REAL': - val = float(val) - elif typ == 'STRING': - val = val[1:-1] - loc = SourceLocation(filename, line, mo.start() - line_start, mo.end() - mo.start()) - yield Token(typ, val, loc) - pos = mo.end() - mo = gettok(s, pos) - if pos != len(s): - col = pos - line_start - loc = SourceLocation(filename, line, col, 1) - raise CompilerError('Unexpected character "{0}"'.format(s[pos]), loc) - loc = SourceLocation(filename, line, 0, 0) - yield Token('END', '', loc)
--- a/python/c3/parser.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,412 +0,0 @@ -import logging -from .lexer import Lexer -from .astnodes import FieldRef, 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 ppci import CompilerError - - -class Parser: - """ Parses sourcecode into an abstract syntax tree (AST) """ - def __init__(self, diag): - self.logger = logging.getLogger('c3') - self.diag = diag - self.lexer = Lexer(diag) - - def parseSource(self, source): - self.logger.info('Parsing source') - self.initLex(source) - try: - self.parsePackage() - return self.mod - except CompilerError as e: - self.diag.addDiag(e) - - def Error(self, msg): - raise CompilerError(msg, self.token.loc) - - # Lexer helpers: - def Consume(self, typ): - if self.Peak == typ: - return self.NextToken() - else: - self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak)) - - @property - def Peak(self): - return self.token.typ - - @property - def CurLoc(self): - return self.token.loc - - def hasConsumed(self, typ): - if self.Peak == typ: - self.Consume(typ) - return True - return False - - def NextToken(self): - t = self.token - if t.typ != 'END': - 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) - - def parseImport(self): - self.Consume('import') - name = self.Consume('ID').val - self.mod.imports.append(name) - self.Consume(';') - - def parsePackage(self): - self.Consume('module') - name = self.Consume('ID') - self.Consume(';') - self.mod = Package(name.val, name.loc) - self.currentPart = self.mod - while self.Peak != 'END': - self.parseTopLevel() - self.Consume('END') - - def parseTopLevel(self): - if self.Peak == 'function': - self.parseFunctionDef() - elif self.Peak == 'var': - self.parseVarDef() - elif self.Peak == 'const': - self.parseConstDef() - elif self.Peak == 'type': - self.parseTypeDef() - elif self.Peak == 'import': - self.parseImport() - else: - self.Error('Expected function, var, const or type') - - def parseDesignator(self): - """ A designator designates an object """ - 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) - - # Type system - def parseTypeSpec(self): - # For now, do simple type spec, just parse an ID: - #return self.parseDesignator() - if self.Peak == 'struct': - self.Consume('struct') - self.Consume('{') - 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)) - self.Consume(';') - self.Consume('}') - theT = StructureType(mems) - else: - theT = self.parseDesignator() - # Check for pointer suffix: - while self.hasConsumed('*'): - theT = PointerType(theT) - return theT - - def parseTypeDef(self): - self.Consume('type') - newtype = self.parseTypeSpec() - typename = self.Consume('ID') - self.Consume(';') - df = DefinedType(typename.val, newtype, typename.loc) - self.addDeclaration(df) - - # Variable declarations: - def parseVarDef(self): - self.Consume('var') - t = self.parseTypeSpec() - def parseVar(): - name = self.Consume('ID') - 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(';') - - def parseConstDef(self): - self.Consume('const') - t = self.parseTypeSpec() - def parseConst(): - name = self.Consume('ID') - self.Consume('=') - val = self.Expression() - c = Constant(name.val, t, val) - c.loc = name.loc - parseConst() - while self.hasConsumed(','): - parseConst() - self.Consume(';') - - # Procedures - def parseFunctionDef(self): - loc = self.Consume('function').loc - returntype = self.parseTypeSpec() - fname = self.Consume('ID').val - f = Function(fname, loc) - self.addDeclaration(f) - savePart = self.currentPart - self.currentPart = f - self.Consume('(') - parameters = [] - if not self.hasConsumed(')'): - def parseParameter(): - 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() - 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 - return IfStatement(condition, yes, no, loc) - - def parseWhileStatement(self): - loc = self.Consume('while').loc - self.Consume('(') - condition = self.Expression() - self.Consume(')') - statements = self.parseCompoundStatement() - return WhileStatement(condition, statements, loc) - - def parseReturnStatement(self): - loc = self.Consume('return').loc - if self.Peak == ';': - expr = Literal(0, loc) - else: - expr = self.Expression() - self.Consume(';') - return ReturnStatement(expr, loc) - - def parseCompoundStatement(self): - self.Consume('{') - statements = [] - while not self.hasConsumed('}'): - s = self.Statement() - if s is None: - continue - statements.append(s) - return CompoundStatement(statements) - - def Statement(self): - # Determine statement type based on the pending token: - if self.Peak == 'if': - return self.parseIfStatement() - elif self.Peak == 'while': - return self.parseWhileStatement() - elif self.Peak == '{': - return self.parseCompoundStatement() - elif self.hasConsumed(';'): - pass - elif self.Peak == 'var': - 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) - - # Expression section: - # We not implement these C constructs: - # a(2), f = 2 - # and this: - # a = 2 < x : 4 ? 1; - - def Expression(self): - exp = self.LogicalAndExpression() - while self.Peak == 'or': - loc = self.Consume('or').loc - e2 = self.LogicalAndExpression() - exp = Binop(exp, 'or', e2, loc) - return exp - - def LogicalAndExpression(self): - o = self.EqualityExpression() - while self.Peak == 'and': - loc = self.Consume('and').loc - o2 = self.EqualityExpression() - o = Binop(o, 'and', o2, loc) - return o - - def EqualityExpression(self): - ee = self.SimpleExpression() - while self.Peak in ['<', '==', '>']: - op = self.Consume(self.Peak) - ee2 = self.SimpleExpression() - ee = Binop(ee, op.typ, ee2, op.loc) - return ee - - def SimpleExpression(self): - """ Shift operations before + and - ? """ - e = self.AddExpression() - while self.Peak in ['>>', '<<']: - op = self.Consume(self.Peak) - e2 = self.AddExpression() - e = Binop(e, op.typ, e2, op.loc) - return e - - def AddExpression(self): - e = self.Term() - while self.Peak in ['+', '-']: - op = self.Consume(self.Peak) - e2 = self.Term() - e = Binop(e, op.typ, e2, op.loc) - return e - - def Term(self): - t = self.BitwiseOr() - while self.Peak in ['*', '/']: - op = self.Consume(self.Peak) - t2 = self.BitwiseOr() - t = Binop(t, op.typ, t2, op.loc) - return t - - def BitwiseOr(self): - a = self.BitwiseAnd() - while self.Peak in ['|']: - op = self.Consume(self.Peak) - b = self.BitwiseAnd() - a = Binop(a, op.typ, b, op.loc) - return a - - def BitwiseAnd(self): - a = self.CastExpression() - while self.Peak in ['&']: - op = self.Consume(self.Peak) - b = self.CastExpression() - a = Binop(a, op.typ, b, op.loc) - return a - - # Domain of unary expressions: - - def CastExpression(self): - """ - the C-style type cast conflicts with '(' expr ')' - so introduce extra keyword 'cast' - """ - if self.Peak == 'cast': - loc = self.Consume('cast').loc - self.Consume('<') - t = self.parseTypeSpec() - self.Consume('>') - self.Consume('(') - ce = self.Expression() - self.Consume(')') - return TypeCast(t, ce, loc) - else: - return self.UnaryExpression() - - def UnaryExpression(self): - if self.Peak in ['&', '*']: - op = self.Consume(self.Peak) - ce = self.CastExpression() - if op.val == '*': - return Deref(ce, op.loc) - else: - return Unop(op.typ, ce, op.loc) - else: - return self.PostFixExpression() - - def PostFixExpression(self): - pfe = self.PrimaryExpression() - while self.Peak in ['[', '(', '.', '->']: - if self.hasConsumed('['): - pass - elif self.hasConsumed('('): - # Function call - args = [] - if not self.hasConsumed(')'): - args.append(self.Expression()) - while self.hasConsumed(','): - args.append(self.Expression()) - self.Consume(')') - pfe = FunctionCall(pfe, args, pfe.loc) - elif self.hasConsumed('->'): - field = self.Consume('ID') - pfe = Deref(pfe, pfe.loc) - pfe = FieldRef(pfe, field.val, field.loc) - elif self.hasConsumed('.'): - field = self.Consume('ID') - pfe = FieldRef(pfe, field.val, field.loc) - else: - raise Exception() - return pfe - - def PrimaryExpression(self): - if self.hasConsumed('('): - e = self.Expression() - self.Consume(')') - return e - elif self.Peak == 'NUMBER': - val = self.Consume('NUMBER') - return Literal(val.val, val.loc) - elif self.Peak == 'REAL': - val = self.Consume('REAL') - return Literal(val.val, val.loc) - elif self.Peak == 'true': - val = self.Consume('true') - return Literal(True, val.loc) - elif self.Peak == 'false': - val = self.Consume('false') - return Literal(False, val.loc) - elif self.Peak == 'ID': - d = self.parseDesignator() - return VariableUse(d, d.loc) - self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak))
--- a/python/c3/scope.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -from . import astnodes - - -class Scope: - """ A scope contains all symbols in a scope """ - def __init__(self, parent=None): - self.symbols = {} - self.parent = parent - - def __iter__(self): - # Iterate in a deterministic manner: - return iter(self.Constants + self.Variables + self.Functions) - - @property - def Syms(self): - syms = self.symbols.values() - return sorted(syms, key=lambda v: v.name) - - @property - def Constants(self): - return [s for s in self.Syms if type(s) is astnodes.Constant] - - @property - def Variables(self): - return [s for s in self.Syms if isinstance(s, astnodes.Variable)] - - @property - def Functions(self): - return [s for s in self.Syms if type(s) is astnodes.Function] - - def getSymbol(self, name): - if name in self.symbols: - return self.symbols[name] - # Look for symbol: - if self.parent: - return self.parent.getSymbol(name) - raise CompilerException("Symbol {0} not found".format(name), name.loc) - - def hasSymbol(self, name): - if name in self.symbols: - return True - if self.parent: - return self.parent.hasSymbol(name) - return False - - def addSymbol(self, sym): - self.symbols[sym.name] = sym - - def __repr__(self): - return 'Scope with {} symbols'.format(len(self.symbols)) - - -def createBuiltins(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)
--- a/python/c3/visitor.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -from .astnodes import * - - -class Visitor: - """ - Visitor that can visit all nodes in the AST - and run pre and post functions. - """ - def visit(self, node, f_pre=None, f_post=None): - self.f_pre = f_pre - self.f_post = f_post - self.do(node) - - def do(self, node): - # Run visitor: - if self.f_pre: - self.f_pre(node) - - # Descent into subnodes: - if type(node) is Package: - for decl in node.declarations: - self.do(decl) - elif type(node) is Function: - for s in node.declarations: - self.do(s) - self.do(node.body) - elif type(node) is CompoundStatement: - for s in node.statements: - self.do(s) - elif type(node) is IfStatement: - self.do(node.condition) - self.do(node.truestatement) - if node.falsestatement: - self.do(node.falsestatement) - elif type(node) is FunctionCall: - for arg in node.args: - self.do(arg) - elif type(node) is Assignment: - self.do(node.lval) - self.do(node.rval) - elif type(node) is ReturnStatement: - self.do(node.expr) - elif type(node) is Binop: - self.do(node.a) - self.do(node.b) - elif type(node) is Unop: - self.do(node.a) - elif type(node) is ExpressionStatement: - self.do(node.ex) - elif type(node) is TypeCast: - self.do(node.a) - elif type(node) is FieldRef: - 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]: - # Those nodes do not have child nodes. - pass - elif type(node) is WhileStatement: - self.do(node.condition) - self.do(node.statement) - else: - raise Exception('Could not visit "{0}"'.format(node)) - - # run post function - if self.f_post: - self.f_post(node) - - -class AstPrinter: - """ Prints an AST as text """ - def printAst(self, pkg): - self.indent = 0 - visitor = Visitor() - visitor.visit(pkg, self.print1, self.print2) - - def print1(self, node): - print(' ' * self.indent + str(node)) - self.indent += 2 - - def print2(self, node): - self.indent -= 2
--- a/python/c3/wishes/coro.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -module coro; - -/* - Some co-routines doing some work. - - This example demonstrates how to write co-operating - routines in a nice way. -*/ - -int regbank[10]; - -function i2c_write(int address, int d) -{ - regbank[2] = address; - regbank[0] |= 0x1; // Issue go command - while (regbank[1] != 0) - { - yield; - } - - // Wait while busy: - while (regbank[1] != 11) - { - yield; - } -} - -function int i2c_read(int address) -{ - regbank[2] = address; - regbank[0] |= 0x1; // Issue go command - while (regbank[1] != 0) - { - yield; - } - - // Wait while busy: - while (regbank[1] != 11) - { - yield; - } -} - -function void eeprom_set(int address, int v) -{ - i2c_write(address, v); - i2c_read(address); -} - -function int calcX(int y) -{ - var int x; - x = 2; - x = x + 2 + 9 * y; - return x; -} - -var int counter = 0; - -task task1() -{ - start task3; - var int a = 200; - while (a > 0) - { - yield; - } -} - -task task2() -{ - while(true) - { - yield; - } -} - -task task3() -{ - eeprom_set(99, 1); - yield; -} - -task main() -{ - start task1; - start task2; - await task1; - await task2; -} -
--- a/python/codegen/canon.py Sun Dec 01 18:37:23 2013 +0100 +++ b/python/codegen/canon.py Tue Dec 03 18:00:22 2013 +0100 @@ -27,6 +27,7 @@ # Rewrite rewrites call instructions into Eseq instructions. + def rewriteStmt(stmt, frame): if isinstance(stmt, ir.Jump): pass @@ -99,6 +100,7 @@ else: raise NotImplementedError('NI: {}'.format(exp)) + def flattenStmt(stmt): if isinstance(stmt, ir.Jump): return [stmt] @@ -125,4 +127,3 @@ """ i = list(flattenStmt(s) for s in block.instructions) block.instructions = list(chain.from_iterable(i)) -
--- a/python/codegen/registerallocator.py Sun Dec 01 18:37:23 2013 +0100 +++ b/python/codegen/registerallocator.py Tue Dec 03 18:00:22 2013 +0100 @@ -4,6 +4,7 @@ # Nifty first function: first = lambda x: next(iter(x)) + class RegisterAllocator: """ Target independent register allocator. @@ -67,7 +68,7 @@ def NodeMoves(self, n): return n.moves & (self.activeMoves | self.worklistMoves) - + def MoveRelated(self, n): return bool(self.NodeMoves(n)) @@ -108,7 +109,7 @@ if m in self.activeMoves: self.activeMoves.remove(m) self.worklistMoves.add(m) - + def Coalesc(self): """ Coalesc conservative. """ m = first(self.worklistMoves) @@ -126,7 +127,7 @@ self.f.ig.Combine(u, v) else: self.activeMoves.add(m) - + def Conservative(self, u, v): """ Briggs conservative criteria for coalesc """ nodes = u.Adjecent | v.Adjecent @@ -149,14 +150,14 @@ self.frozenMoves.add(m) # Check other part of the move for still being move related: v = m.src[0] if u is m.dst[0] else m.dst[0] - + def SelectSpill(self): # TODO pass def AssignColors(self): """ Add nodes back to the graph to color it. """ - # Add nodes back to the graph: + # Add nodes back to the graph: while self.selectStack: n = self.selectStack.pop(-1) self.f.ig.addNode(n)
--- a/python/mem2reg.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -import logging -from transform import FunctionPass -from ir import * - -def isAllocPromotable(allocinst): - # Check if alloc value is only used by load and store operations. - assert type(allocinst) is Alloc - return all(type(use) in [Load, Store] for use in allocinst.value.used_by) - - -class Mem2RegPromotor(FunctionPass): - def promoteSingleBlock(self, ai): - v = ai.value - bb = ai.Block - - # Replace all loads with the value: - loads = [i for i in v.used_by if isinstance(i, Load)] - stores = [i for i in v.used_by if isinstance(i, Store)] - stores.sort(key=lambda s: s.Position) - stores.reverse() - - for load in loads: - idx = load.Position - # Search upwards: - for store in stores: - if store.Position < load.Position: - break - load.value.replaceby(store.value) - logging.debug('replaced {} with {}'.format(load, store.value)) - bb.removeInstruction(load) - - # Remove store instructions: - for store in stores: - sv = store.value - logging.debug('removing {}'.format(store)) - bb.removeInstruction(store) - #assert sv.Used - - # Remove alloca instruction: - assert not ai.value.Used, ai.value.used_by - bb.removeInstruction(ai) - - def promote(self, ai): - # Find load operations and replace them with assignments - v = ai.value - if len(ai.value.UsedInBlocks) == 1: - self.promoteSingleBlock(ai) - return - - loads = [i for i in v.used_by if isinstance(i, Load)] - stores = [i for i in v.used_by if isinstance(i, Store)] - - # Each store instruction can be removed (later). - # Instead of storing the value, we use it - # where the load would have been! - replMap = {} - for store in stores: - replMap[store] = store.value - - # for each load, track back what the defining store - # was. - for load in loads: - pass - - def onFunction(self, f): - for bb in f.BasicBlocks: - allocs = [i for i in bb.Instructions if isinstance(i, Alloc)] - for i in allocs: - if isAllocPromotable(i): - self.promote(i)
--- a/python/optimize.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -from mem2reg import Mem2RegPromotor -from transform import CommonSubexpressionElimination, CleanPass -from transform import DeadCodeDeleter, ConstantFolder - -def optimize(ir): - return - cf = ConstantFolder() - cf.run(ir) - return - dcd = DeadCodeDeleter() - m2r = Mem2RegPromotor() - clr = CleanPass() - cse = CommonSubexpressionElimination() - dcd.run(ir) - clr.run(ir) - m2r.run(ir) - cse.run(ir) - cf.run(ir) - dcd.run(ir)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/__init__.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,9 @@ +""" This is the C3 language front end. """ + + +from .parser import Parser +from .lexer import Lexer +from .analyse import Analyzer, TypeChecker +from .codegenerator import CodeGenerator +from .visitor import Visitor, AstPrinter +from .builder import Builder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/analyse.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,328 @@ +import logging +from .visitor import Visitor +from .astnodes import * +from .scope import * + + +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.diag.error(msg, loc) + + def visit(self, pkg, pre, post): + self.visitor.visit(pkg, pre, post) + + +class AddScope(C3Pass): + """ 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] + 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): + return self.scopeStack[-1] + + def addSymbol(self, sym): + if self.CurrentScope.hasSymbol(sym.name): + self.error('Redefinition of {0}'.format(sym.name), sym.loc) + else: + self.CurrentScope.addSymbol(sym) + + def enterScope(self, sym): + # Distribute the scope: + sym.scope = self.CurrentScope + + # Add symbols to current scope: + if isinstance(sym, Symbol) or isinstance(sym, DefinedType): + self.addSymbol(sym) + + # Create subscope: + if type(sym) in [Package, Function]: + 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]: + self.scopeStack.pop(-1) + + +class Analyzer(C3Pass): + """ + Context handling is done here. + Scope is attached to the correct modules. + This class checks names and references. + """ + + def analyzePackage(self, pkg, packageDict): + self.ok = True + # Prepare top level scope and set scope to all objects: + + self.logger.info('Resolving imports for package {}'.format(pkg.name)) + # Handle imports: + for i in pkg.imports: + if i not in packageDict: + self.error('Cannot import {}'.format(i)) + continue + ip = packageDict[i] + pkg.scope.addSymbol(ip) + FixRefs(self.diag).fixRefs(pkg) + return self.ok + + +class FixRefs(C3Pass): + def fixRefs(self, pkg): + 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 + print(varuse) + 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) + + +# Type checking: + +def theType(t): + """ Recurse until a 'real' type is found """ + if type(t) is DefinedType: + return theType(t.typ) + return t + + +def equalTypes(a, b): + """ Compare types a and b for structural equavalence. """ + # Recurse into named types: + a, b = theType(a), theType(b) + + if type(a) is type(b): + if type(a) is BaseType: + return a.name == b.name + elif type(a) is PointerType: + return equalTypes(a.ptype, b.ptype) + elif type(a) is StructureType: + if len(a.mems) != len(b.mems): + return False + 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))) + return False + + +def canCast(fromT, toT): + fromT = theType(fromT) + toT = theType(toT) + if isinstance(fromT, PointerType) and isinstance(toT, PointerType): + return True + elif fromT is intType and isinstance(toT, PointerType): + return True + return False + + +def expectRval(s): + # TODO: solve this better + s.expect_rvalue = True + + +class TypeChecker(C3Pass): + def checkPackage(self, pkg): + self.logger.info('Type checking {}'.format(pkg.name)) + self.ok = True + self.visit(pkg, None, self.check2) + return self.ok + + 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) + self.error(msg, sym.condition.loc) + elif type(sym) is Assignment: + l, r = sym.lval, sym.rval + if not equalTypes(l.typ, r.typ): + msg = 'Cannot assign {} to {}'.format(r.typ, l.typ) + self.error(msg, sym.loc) + if not l.lvalue: + self.error('No valid lvalue {}'.format(l), l.loc) + #if sym.rval.lvalue: + # self.error('Right hand side must be an rvalue', sym.rval.loc) + expectRval(sym.rval) + elif type(sym) is ReturnStatement: + pass + elif type(sym) is FunctionCall: + # Check arguments: + ngiv = len(sym.args) + ptypes = sym.proc.typ.parametertypes + nreq = len(ptypes) + if ngiv != nreq: + self.error('Function {2}: {0} arguments required, {1} given'.format(nreq, ngiv, sym.proc.name), sym.loc) + else: + for a, at in zip(sym.args, ptypes): + expectRval(a) + if not equalTypes(a.typ, at): + self.error('Got {0}, expected {1}'.format(a.typ, at), a.loc) + # determine return type: + sym.typ = sym.proc.typ.returntype + elif type(sym) is VariableUse: + sym.lvalue = True + if isinstance(sym.target, Variable): + sym.typ = sym.target.typ + else: + print('warning {} has no target, defaulting to int'.format(sym)) + sym.typ = intType + 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 + else: + raise Exception('Unknown literal type'.format(sym.val)) + elif type(sym) is Unop: + if sym.op == '&': + sym.typ = PointerType(sym.a.typ) + sym.lvalue = False + else: + raise Exception('Unknown unop {0}'.format(sym.op)) + elif type(sym) is Deref: + # pointer deref + sym.lvalue = True + # check if the to be dereferenced variable is a pointer type: + 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: + basetype = sym.base.typ + sym.lvalue = sym.base.lvalue + basetype = theType(basetype) + if type(basetype) is StructureType: + if basetype.hasField(sym.field): + sym.typ = basetype.fieldType(sym.field) + else: + self.error('{} does not contain field {}'.format(basetype, sym.field), sym.loc) + sym.typ = intType + else: + self.error('Cannot select field {} of non-structure type {}'.format(sym.field, basetype), sym.loc) + sym.typ = intType + elif type(sym) is Binop: + 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): + sym.typ = sym.a.typ + else: + self.error('Can only add integers', sym.loc) + sym.typ = intType + else: + # assume void here? TODO: throw exception! + sym.typ = 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 + 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) + else: + raise Exception('Unknown binop {0}'.format(sym.op)) + elif isinstance(sym, Variable): + # check initial value type: + # TODO + pass + elif type(sym) is TypeCast: + 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 + 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]: + pass + else: + raise NotImplementedError('Unknown type check {0}'.format(sym))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/astnodes.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,361 @@ +""" +AST (abstract syntax tree) nodes for the c3 language. +The tree is build by the parser. +Then it is checked +Finally code is generated from it. +""" + +from ppci import SourceLocation + + +class Node: + pass + + +# Variables, parameters, local variables, constants and named types: +class Symbol(Node): + def __init__(self, name): + self.name = name + self.refs = [] + + def addRef(self, r): + self.refs.append(r) + + @property + def References(self): + return self.refs + + +# Modules +class Package(Node): + def __init__(self, name, loc): + self.name = name + self.loc = loc + self.declarations = [] + self.imports = [] + + def __repr__(self): + 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): + pass + + +class NamedType(Type, Symbol): + def __init__(self, name): + Symbol.__init__(self, name) + + +class BaseType(NamedType): + def __init__(self, name): + super().__init__(name) + + def __repr__(self): + return '{}'.format(self.name) + + +class FunctionType(Type): + def __init__(self, parametertypes, returntype): + self.parametertypes = parametertypes + self.returntype = returntype + + def __repr__(self): + params = ', '.join([str(v) for v in self.parametertypes]) + return '{1} f({0})'.format(params, self.returntype) + + +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) + self.ptype = ptype + + def __repr__(self): + return '({}*)'.format(self.ptype) + + +class StructField: + def __init__(self, name, typ): + self.name = name + self.typ = typ + self.offset = 0 + + +class StructureType(Type): + def __init__(self, mems): + self.mems = mems + for mem in mems: + assert type(mem) is StructField + assert type(mem.name) is str + + def hasField(self, name): + for mem in self.mems: + if name == mem.name: + return True + return False + + def fieldType(self, name): + return self.findField(name).typ + + def fieldOffset(self, name): + return self.findField(name).offset + + def findField(self, name): + for mem in self.mems: + if name == mem.name: + return mem + raise KeyError(name) + + def __repr__(self): + return 'STRUCT' + + +class DefinedType(NamedType): + """ A named type indicating another type """ + def __init__(self, name, typ, loc): + assert isinstance(name, str) + super().__init__(name) + self.typ = typ + self.loc = loc + + def __repr__(self): + return 'Named type {0} of type {1}'.format(self.name, self.typ) + + +class Constant(Symbol): + 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): + super().__init__(name) + self.typ = typ + self.ival = None + self.isLocal = False + self.isReadOnly = False + self.isParameter = False + + def __repr__(self): + return 'Var {} [{}]'.format(self.name, self.typ) + + +class LocalVariable(Variable): + def __init__(self, name, typ): + super().__init__(name, typ) + self.isLocal = True + + +class FormalParameter(Variable): + def __init__(self, name, typ): + super().__init__(name, typ) + self.isParameter = True + + +# Procedure types +class Function(Symbol): + """ Actual implementation of a function """ + def __init__(self, name, loc): + super().__init__(name) + self.loc = loc + self.declarations = [] + + def __repr__(self): + return 'Func {}'.format(self.name) + + +# Operations / Expressions: +class Expression(Node): + def __init__(self, loc): + self.loc = loc + + +class Deref(Expression): + def __init__(self, ptr, loc): + super().__init__(loc) + assert isinstance(ptr, Expression) + self.ptr = ptr + + def __repr__(self): + return 'DEREF {}'.format(self.ptr) + + +class TypeCast(Expression): + def __init__(self, to_type, x, loc): + super().__init__(loc) + self.to_type = to_type + self.a = x + + def __repr__(self): + return 'TYPECAST {}'.format(self.to_type) + + +class FieldRef(Expression): + def __init__(self, base, field, loc): + super().__init__(loc) + assert isinstance(base, Expression) + assert isinstance(field, str) + self.base = base + self.field = field + + def __repr__(self): + return 'FIELD {}.{}'.format(self.base, self.field) + + +class Unop(Expression): + def __init__(self, op, a, loc): + super().__init__(loc) + assert isinstance(a, Expression) + assert isinstance(op, str) + self.a = a + self.op = op + + def __repr__(self): + return 'UNOP {}'.format(self.op) + + +class Binop(Expression): + def __init__(self, a, op, b, loc): + super().__init__(loc) + assert isinstance(a, Expression), type(a) + assert isinstance(b, Expression) + assert isinstance(op, str) + self.a = a + self.b = b + self.op = op # Operation: '+', '-', '*', '/', 'mod' + + def __repr__(self): + return 'BINOP {}'.format(self.op) + + +class VariableUse(Expression): + def __init__(self, target, loc): + super().__init__(loc) + self.target = target + + def __repr__(self): + return 'VAR USE {}'.format(self.target) + + +class Literal(Expression): + def __init__(self, val, loc): + super().__init__(loc) + self.val = val + + def __repr__(self): + return 'LITERAL {}'.format(self.val) + + +class FunctionCall(Expression): + def __init__(self, proc, args, loc): + super().__init__(loc) + self.proc = proc + self.args = args + + def __repr__(self): + return 'CALL {0} '.format(self.proc) + + +# Statements +class Statement(Node): + def __init__(self, loc): + self.loc = loc + + +class CompoundStatement(Statement): + def __init__(self, statements): + super().__init__(None) + self.statements = statements + for s in self.statements: + assert isinstance(s, Statement) + + def __repr__(self): + return 'COMPOUND STATEMENT' + + +class ReturnStatement(Statement): + def __init__(self, expr, loc): + super().__init__(loc) + self.expr = expr + + def __repr__(self): + return 'RETURN STATEMENT' + + +class Assignment(Statement): + def __init__(self, lval, rval, loc): + super().__init__(loc) + assert isinstance(lval, Node) + assert isinstance(rval, Node) + self.lval = lval + self.rval = rval + + def __repr__(self): + return 'ASSIGNMENT' + + +class ExpressionStatement(Statement): + def __init__(self, ex, loc): + super().__init__(loc) + self.ex = ex + + def __repr__(self): + return 'Epression' + + +class IfStatement(Statement): + def __init__(self, condition, truestatement, falsestatement, loc): + super().__init__(loc) + self.condition = condition + self.truestatement = truestatement + self.falsestatement = falsestatement + + def __repr__(self): + return 'IF-statement' + + +class WhileStatement(Statement): + def __init__(self, condition, statement, loc): + super().__init__(loc) + self.condition = condition + self.statement = statement + + def __repr__(self): + return 'WHILE-statement'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/builder.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,66 @@ +import logging +import ppci +from .parser import Parser +from .analyse import TypeChecker, Analyzer +from .codegenerator import CodeGenerator +from .analyse import AddScope + + +class Builder: + """ + Generates IR-code from c3 source. + Reports errors to the diagnostics system. + """ + def __init__(self, diag): + self.logger = logging.getLogger('c3') + self.diag = 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 + + def build(self, srcs, imps=[]): + """ Create IR-code from sources """ + self.logger.info('Starting build with {}'.format(srcs)) + 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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/codegenerator.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,198 @@ +import logging +import ir +from . import astnodes +from .scope import boolType, intType +from ppci import CompilerError +from .analyse import theType + + +class CodeGenerator(ir.Builder): + """ + Generates intermediate (IR) code from a package. The entry function is + 'genModule'. The main task of this part is to rewrite complex control + structures, such as while and for loops into simple conditional + jump statements. Also complex conditional statements are simplified. + Such as 'and' and 'or' statements are rewritten in conditional jumps. + And structured datatypes are rewritten. + """ + def __init__(self): + self.logger = logging.getLogger('c3cgen') + + def gencode(self, pkg): + self.prepare() + assert type(pkg) is astnodes.Package + self.logger.info('Generating ir-code for {}'.format(pkg.name)) + self.varMap = {} # Maps variables to storage locations. + self.funcMap = {} + self.m = ir.Module(pkg.name) + self.genModule(pkg) + return self.m + + # inner helpers: + def genModule(self, pkg): + # Take care of forward declarations: + for s in pkg.innerScope.Functions: + f = self.newFunction(s.name) + self.funcMap[s] = f + for v in pkg.innerScope.Variables: + self.varMap[v] = self.newTemp() + for s in pkg.innerScope.Functions: + self.genFunction(s) + + def genFunction(self, fn): + # 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)) + self.setBlock(l2) + # generate room for locals: + + for sym in fn.innerScope: + # TODO: handle parameters different + if sym.isParameter: + v = ir.Parameter(sym.name) + f.addParameter(v) + elif sym.isLocal: + v = ir.LocalVariable(sym.name) + f.addLocal(v) + else: + #v = self.newTemp() + raise NotImplementedError('{}'.format(sym)) + # TODO: make this ssa here?? + self.varMap[sym] = v + + self.genCode(fn.body) + # Set the default return value to zero: + # TBD: this may not be required? + self.emit(ir.Move(f.return_value, ir.Const(0))) + self.emit(ir.Jump(f.epiloog)) + self.setFunction(None) + + def genCode(self, code): + assert isinstance(code, astnodes.Statement) + self.setLoc(code.loc) + if type(code) is astnodes.CompoundStatement: + for s in code.statements: + self.genCode(s) + elif type(code) is astnodes.Assignment: + rval = self.genExprCode(code.rval) + lval = self.genExprCode(code.lval) + self.emit(ir.Move(lval, rval)) + elif type(code) is astnodes.ExpressionStatement: + self.emit(ir.Exp(self.genExprCode(code.ex))) + elif type(code) is astnodes.IfStatement: + bbtrue = self.newBlock() + bbfalse = self.newBlock() + te = self.newBlock() + self.genCondCode(code.condition, bbtrue, bbfalse) + self.setBlock(bbtrue) + self.genCode(code.truestatement) + self.emit(ir.Jump(te)) + self.setBlock(bbfalse) + if code.falsestatement: + self.genCode(code.falsestatement) + self.emit(ir.Jump(te)) + self.setBlock(te) + elif type(code) is astnodes.ReturnStatement: + if code.expr: + re = self.genExprCode(code.expr) + self.emit(ir.Move(self.fn.return_value, re)) + self.emit(ir.Jump(self.fn.epiloog)) + b = self.newBlock() + self.setBlock(b) + else: + self.builder.addIns(ir.Return()) + elif type(code) is astnodes.WhileStatement: + bbdo = self.newBlock() + bbtest = self.newBlock() + te = self.newBlock() + self.emit(ir.Jump(bbtest)) + self.setBlock(bbtest) + self.genCondCode(code.condition, bbdo, te) + self.setBlock(bbdo) + self.genCode(code.statement) + self.emit(ir.Jump(bbtest)) + self.setBlock(te) + else: + raise NotImplementedError('Unknown stmt {}'.format(code)) + + 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() + self.genCondCode(expr.a, bbtrue, l2) + self.setBlock(l2) + self.genCondCode(expr.b, bbtrue, bbfalse) + elif expr.op == 'and': + l2 = self.newBlock() + self.genCondCode(expr.a, l2, bbfalse) + self.setBlock(l2) + self.genCondCode(expr.b, bbtrue, bbfalse) + elif expr.op in ['==', '>', '<']: + ta = self.genExprCode(expr.a) + tb = self.genExprCode(expr.b) + self.emit(ir.CJump(ta, expr.op, tb, bbtrue, bbfalse)) + else: + raise NotImplementedError('Unknown condition {}'.format(expr)) + elif type(expr) is astnodes.Literal: + if expr.val: + self.emit(ir.Jump(bbtrue)) + else: + self.emit(ir.Jump(bbfalse)) + else: + raise NotImplementedError('Unknown cond {}'.format(expr)) + + def genExprCode(self, expr): + assert isinstance(expr, astnodes.Expression) + if type(expr) is astnodes.Binop and expr.op in ir.Binop.ops: + ra = self.genExprCode(expr.a) + rb = self.genExprCode(expr.b) + return ir.Binop(ra, expr.op, rb) + elif type(expr) is astnodes.Unop and expr.op == '&': + ra = self.genExprCode(expr.a) + assert type(ra) is ir.Mem + return ra.e + elif type(expr) is astnodes.VariableUse: + # This returns the dereferenced variable. + if expr.target.isParameter: + # TODO: now parameters are handled different. Not nice? + return self.varMap[expr.target] + else: + return ir.Mem(self.varMap[expr.target]) + elif type(expr) is astnodes.Deref: + # dereference pointer type: + addr = self.genExprCode(expr.ptr) + return ir.Mem(addr) + elif type(expr) is astnodes.FieldRef: + base = self.genExprCode(expr.base) + assert type(base) is ir.Mem, type(base) + base = base.e + bt = theType(expr.base.typ) + offset = ir.Const(bt.fieldOffset(expr.field)) + return ir.Mem(ir.Add(base, offset)) + elif type(expr) is astnodes.Literal: + return ir.Const(expr.val) + elif type(expr) is astnodes.TypeCast: + # TODO: improve this mess: + ar = self.genExprCode(expr.a) + tt = theType(expr.to_type) + if isinstance(tt, astnodes.PointerType): + if expr.a.typ is intType: + return ar + elif isinstance(expr.a.typ, astnodes.PointerType): + return ar + else: + raise Exception() + else: + raise NotImplementedError("not implemented") + elif type(expr) is astnodes.FunctionCall: + args = [self.genExprCode(e) for e in expr.args] + fn = self.funcMap[expr.proc] + return ir.Call(fn, args) + else: + raise NotImplementedError('Unknown expr {}'.format(expr))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/lexer.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,94 @@ +import collections +import re + +from ppci import CompilerError, SourceLocation, Token + +""" + Lexical analyzer part. Splits the input character stream into tokens. +""" + +keywords = ['and', 'or', 'not', 'true', 'false', + 'else', 'if', 'while', 'return', + 'function', 'var', 'type', 'const', + 'struct', 'cast', + 'import', 'module'] + + +class Lexer: + def __init__(self, diag): + self.diag = diag + + def tokenize(self, input_file): + """ + Tokenizer, generates an iterator that + returns tokens! + + Input is a file like object. + + This GREAT example was taken from python re doc page! + """ + filename = input_file.name if hasattr(input_file, 'name') else '' + s = input_file.read() + input_file.close() + self.diag.addSource(filename, s) + tok_spec = [ + ('REAL', r'\d+\.\d+'), + ('HEXNUMBER', r'0x[\da-fA-F]+'), + ('NUMBER', r'\d+'), + ('ID', r'[A-Za-z][A-Za-z\d_]*'), + ('NEWLINE', r'\n'), + ('SKIP', r'[ \t]'), + ('COMMENTS', r'//.*'), + ('LONGCOMMENTBEGIN', r'\/\*'), + ('LONGCOMMENTEND', r'\*\/'), + ('LEESTEKEN', r'==|->|<<|>>|!=|[\.,=:;\-+*\[\]/\(\)]|>=|<=|<>|>|<|{|}|&|\^|\|'), + ('STRING', r"'.*?'") + ] + tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) + gettok = re.compile(tok_re).match + line = 1 + pos = line_start = 0 + mo = gettok(s) + incomment = False + while mo is not None: + typ = mo.lastgroup + val = mo.group(typ) + if typ == 'NEWLINE': + line_start = pos + line += 1 + elif typ == 'COMMENTS': + pass + elif typ == 'LONGCOMMENTBEGIN': + incomment = True + elif typ == 'LONGCOMMENTEND': + incomment = False + elif typ == 'SKIP': + pass + elif incomment: + pass # Wait until we are not in a comment section + else: + if typ == 'ID': + if val in keywords: + typ = val + elif typ == 'LEESTEKEN': + typ = val + elif typ == 'NUMBER': + val = int(val) + elif typ == 'HEXNUMBER': + val = int(val[2:], 16) + typ = 'NUMBER' + elif typ == 'REAL': + val = float(val) + elif typ == 'STRING': + val = val[1:-1] + loc = SourceLocation(filename, line, mo.start() - line_start, + mo.end() - mo.start()) + yield Token(typ, val, loc) + pos = mo.end() + mo = gettok(s, pos) + if pos != len(s): + col = pos - line_start + loc = SourceLocation(filename, line, col, 1) + raise CompilerError('Unexpected: "{0}"'.format(s[pos]), loc) + loc = SourceLocation(filename, line, 0, 0) + yield Token('END', '', loc)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/parser.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,414 @@ +import logging +from .lexer import Lexer +from .astnodes import FieldRef, 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 ppci import CompilerError + + +class Parser: + """ Parses sourcecode into an abstract syntax tree (AST) """ + def __init__(self, diag): + self.logger = logging.getLogger('c3') + self.diag = diag + self.lexer = Lexer(diag) + + def parseSource(self, source): + self.logger.info('Parsing source') + self.initLex(source) + try: + self.parsePackage() + return self.mod + except CompilerError as e: + self.diag.addDiag(e) + + def Error(self, msg): + raise CompilerError(msg, self.token.loc) + + # Lexer helpers: + def Consume(self, typ): + if self.Peak == typ: + return self.NextToken() + else: + self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak)) + + @property + def Peak(self): + return self.token.typ + + @property + def CurLoc(self): + return self.token.loc + + def hasConsumed(self, typ): + if self.Peak == typ: + self.Consume(typ) + return True + return False + + def NextToken(self): + t = self.token + if t.typ != 'END': + 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) + + def parseImport(self): + self.Consume('import') + name = self.Consume('ID').val + self.mod.imports.append(name) + self.Consume(';') + + def parsePackage(self): + self.Consume('module') + name = self.Consume('ID') + self.Consume(';') + self.mod = Package(name.val, name.loc) + self.currentPart = self.mod + while self.Peak != 'END': + self.parseTopLevel() + self.Consume('END') + + def parseTopLevel(self): + if self.Peak == 'function': + self.parseFunctionDef() + elif self.Peak == 'var': + self.parseVarDef() + elif self.Peak == 'const': + self.parseConstDef() + elif self.Peak == 'type': + self.parseTypeDef() + elif self.Peak == 'import': + self.parseImport() + else: + self.Error('Expected function, var, const or type') + + def parseDesignator(self): + """ A designator designates an object """ + 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) + + # Type system + def parseTypeSpec(self): + # For now, do simple type spec, just parse an ID: + #return self.parseDesignator() + if self.Peak == 'struct': + self.Consume('struct') + self.Consume('{') + 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)) + self.Consume(';') + self.Consume('}') + theT = StructureType(mems) + else: + theT = self.parseDesignator() + # Check for pointer suffix: + while self.hasConsumed('*'): + theT = PointerType(theT) + return theT + + def parseTypeDef(self): + self.Consume('type') + newtype = self.parseTypeSpec() + typename = self.Consume('ID') + self.Consume(';') + df = DefinedType(typename.val, newtype, typename.loc) + self.addDeclaration(df) + + # Variable declarations: + def parseVarDef(self): + self.Consume('var') + t = self.parseTypeSpec() + + def parseVar(): + name = self.Consume('ID') + 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(';') + + def parseConstDef(self): + self.Consume('const') + t = self.parseTypeSpec() + + def parseConst(): + name = self.Consume('ID') + self.Consume('=') + val = self.Expression() + c = Constant(name.val, t, val) + c.loc = name.loc + parseConst() + while self.hasConsumed(','): + parseConst() + self.Consume(';') + + # Procedures + def parseFunctionDef(self): + loc = self.Consume('function').loc + returntype = self.parseTypeSpec() + fname = self.Consume('ID').val + f = Function(fname, loc) + self.addDeclaration(f) + savePart = self.currentPart + self.currentPart = f + self.Consume('(') + parameters = [] + if not self.hasConsumed(')'): + def parseParameter(): + 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() + 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 + return IfStatement(condition, yes, no, loc) + + def parseWhileStatement(self): + loc = self.Consume('while').loc + self.Consume('(') + condition = self.Expression() + self.Consume(')') + statements = self.parseCompoundStatement() + return WhileStatement(condition, statements, loc) + + def parseReturnStatement(self): + loc = self.Consume('return').loc + if self.Peak == ';': + expr = Literal(0, loc) + else: + expr = self.Expression() + self.Consume(';') + return ReturnStatement(expr, loc) + + def parseCompoundStatement(self): + self.Consume('{') + statements = [] + while not self.hasConsumed('}'): + s = self.Statement() + if s is None: + continue + statements.append(s) + return CompoundStatement(statements) + + def Statement(self): + # Determine statement type based on the pending token: + if self.Peak == 'if': + return self.parseIfStatement() + elif self.Peak == 'while': + return self.parseWhileStatement() + elif self.Peak == '{': + return self.parseCompoundStatement() + elif self.hasConsumed(';'): + pass + elif self.Peak == 'var': + 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) + + # Expression section: + # We not implement these C constructs: + # a(2), f = 2 + # and this: + # a = 2 < x : 4 ? 1; + + def Expression(self): + exp = self.LogicalAndExpression() + while self.Peak == 'or': + loc = self.Consume('or').loc + e2 = self.LogicalAndExpression() + exp = Binop(exp, 'or', e2, loc) + return exp + + def LogicalAndExpression(self): + o = self.EqualityExpression() + while self.Peak == 'and': + loc = self.Consume('and').loc + o2 = self.EqualityExpression() + o = Binop(o, 'and', o2, loc) + return o + + def EqualityExpression(self): + ee = self.SimpleExpression() + while self.Peak in ['<', '==', '>', '>=', '<=', '!=']: + op = self.Consume(self.Peak) + ee2 = self.SimpleExpression() + ee = Binop(ee, op.typ, ee2, op.loc) + return ee + + def SimpleExpression(self): + """ Shift operations before + and - ? """ + e = self.AddExpression() + while self.Peak in ['>>', '<<']: + op = self.Consume(self.Peak) + e2 = self.AddExpression() + e = Binop(e, op.typ, e2, op.loc) + return e + + def AddExpression(self): + e = self.Term() + while self.Peak in ['+', '-']: + op = self.Consume(self.Peak) + e2 = self.Term() + e = Binop(e, op.typ, e2, op.loc) + return e + + def Term(self): + t = self.BitwiseOr() + while self.Peak in ['*', '/']: + op = self.Consume(self.Peak) + t2 = self.BitwiseOr() + t = Binop(t, op.typ, t2, op.loc) + return t + + def BitwiseOr(self): + a = self.BitwiseAnd() + while self.Peak in ['|']: + op = self.Consume(self.Peak) + b = self.BitwiseAnd() + a = Binop(a, op.typ, b, op.loc) + return a + + def BitwiseAnd(self): + a = self.CastExpression() + while self.Peak in ['&']: + op = self.Consume(self.Peak) + b = self.CastExpression() + a = Binop(a, op.typ, b, op.loc) + return a + + # Domain of unary expressions: + + def CastExpression(self): + """ + the C-style type cast conflicts with '(' expr ')' + so introduce extra keyword 'cast' + """ + if self.Peak == 'cast': + loc = self.Consume('cast').loc + self.Consume('<') + t = self.parseTypeSpec() + self.Consume('>') + self.Consume('(') + ce = self.Expression() + self.Consume(')') + return TypeCast(t, ce, loc) + else: + return self.UnaryExpression() + + def UnaryExpression(self): + if self.Peak in ['&', '*']: + op = self.Consume(self.Peak) + ce = self.CastExpression() + if op.val == '*': + return Deref(ce, op.loc) + else: + return Unop(op.typ, ce, op.loc) + else: + return self.PostFixExpression() + + def PostFixExpression(self): + pfe = self.PrimaryExpression() + while self.Peak in ['[', '(', '.', '->']: + if self.hasConsumed('['): + pass + elif self.hasConsumed('('): + # Function call + args = [] + if not self.hasConsumed(')'): + args.append(self.Expression()) + while self.hasConsumed(','): + args.append(self.Expression()) + self.Consume(')') + pfe = FunctionCall(pfe, args, pfe.loc) + elif self.hasConsumed('->'): + field = self.Consume('ID') + pfe = Deref(pfe, pfe.loc) + pfe = FieldRef(pfe, field.val, field.loc) + elif self.hasConsumed('.'): + field = self.Consume('ID') + pfe = FieldRef(pfe, field.val, field.loc) + else: + raise Exception() + return pfe + + def PrimaryExpression(self): + if self.hasConsumed('('): + e = self.Expression() + self.Consume(')') + return e + elif self.Peak == 'NUMBER': + val = self.Consume('NUMBER') + return Literal(val.val, val.loc) + elif self.Peak == 'REAL': + val = self.Consume('REAL') + return Literal(val.val, val.loc) + elif self.Peak == 'true': + val = self.Consume('true') + return Literal(True, val.loc) + elif self.Peak == 'false': + val = self.Consume('false') + return Literal(False, val.loc) + elif self.Peak == 'ID': + d = self.parseDesignator() + return VariableUse(d, d.loc) + self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/scope.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,70 @@ +from . import astnodes + + +class Scope: + """ A scope contains all symbols in a scope """ + def __init__(self, parent=None): + self.symbols = {} + self.parent = parent + + def __iter__(self): + # Iterate in a deterministic manner: + return iter(self.Constants + self.Variables + self.Functions) + + @property + def Syms(self): + syms = self.symbols.values() + return sorted(syms, key=lambda v: v.name) + + @property + def Constants(self): + return [s for s in self.Syms if type(s) is astnodes.Constant] + + @property + def Variables(self): + return [s for s in self.Syms if isinstance(s, astnodes.Variable)] + + @property + def Functions(self): + return [s for s in self.Syms if type(s) is astnodes.Function] + + def getSymbol(self, name): + if name in self.symbols: + return self.symbols[name] + # Look for symbol: + if self.parent: + return self.parent.getSymbol(name) + raise CompilerException("Symbol {0} not found".format(name), name.loc) + + def hasSymbol(self, name): + if name in self.symbols: + return True + if self.parent: + return self.parent.hasSymbol(name) + return False + + def addSymbol(self, sym): + self.symbols[sym.name] = sym + + def __repr__(self): + return 'Scope with {} symbols'.format(len(self.symbols)) + + +def createBuiltins(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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/c3/visitor.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,85 @@ +from .astnodes import * + + +class Visitor: + """ + Visitor that can visit all nodes in the AST + and run pre and post functions. + """ + def visit(self, node, f_pre=None, f_post=None): + self.f_pre = f_pre + self.f_post = f_post + self.do(node) + + def do(self, node): + # Run visitor: + if self.f_pre: + self.f_pre(node) + + # Descent into subnodes: + if type(node) is Package: + for decl in node.declarations: + self.do(decl) + elif type(node) is Function: + for s in node.declarations: + self.do(s) + self.do(node.body) + elif type(node) is CompoundStatement: + for s in node.statements: + self.do(s) + elif type(node) is IfStatement: + self.do(node.condition) + self.do(node.truestatement) + if node.falsestatement: + self.do(node.falsestatement) + elif type(node) is FunctionCall: + for arg in node.args: + self.do(arg) + elif type(node) is Assignment: + self.do(node.lval) + self.do(node.rval) + elif type(node) is ReturnStatement: + self.do(node.expr) + elif type(node) is Binop: + self.do(node.a) + self.do(node.b) + elif type(node) is Unop: + self.do(node.a) + elif type(node) is ExpressionStatement: + self.do(node.ex) + elif type(node) is TypeCast: + self.do(node.a) + elif type(node) is FieldRef: + 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]: + # Those nodes do not have child nodes. + pass + elif type(node) is WhileStatement: + self.do(node.condition) + self.do(node.statement) + else: + raise Exception('Could not visit "{0}"'.format(node)) + + # run post function + if self.f_post: + self.f_post(node) + + +class AstPrinter: + """ Prints an AST as text """ + def printAst(self, pkg): + self.indent = 0 + visitor = Visitor() + visitor.visit(pkg, self.print1, self.print2) + + def print1(self, node): + print(' ' * self.indent + str(node)) + self.indent += 2 + + def print2(self, node): + self.indent -= 2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/mem2reg.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,70 @@ +import logging +from transform import FunctionPass +from ir import * + +def isAllocPromotable(allocinst): + # Check if alloc value is only used by load and store operations. + assert type(allocinst) is Alloc + return all(type(use) in [Load, Store] for use in allocinst.value.used_by) + + +class Mem2RegPromotor(FunctionPass): + def promoteSingleBlock(self, ai): + v = ai.value + bb = ai.Block + + # Replace all loads with the value: + loads = [i for i in v.used_by if isinstance(i, Load)] + stores = [i for i in v.used_by if isinstance(i, Store)] + stores.sort(key=lambda s: s.Position) + stores.reverse() + + for load in loads: + idx = load.Position + # Search upwards: + for store in stores: + if store.Position < load.Position: + break + load.value.replaceby(store.value) + logging.debug('replaced {} with {}'.format(load, store.value)) + bb.removeInstruction(load) + + # Remove store instructions: + for store in stores: + sv = store.value + logging.debug('removing {}'.format(store)) + bb.removeInstruction(store) + #assert sv.Used + + # Remove alloca instruction: + assert not ai.value.Used, ai.value.used_by + bb.removeInstruction(ai) + + def promote(self, ai): + # Find load operations and replace them with assignments + v = ai.value + if len(ai.value.UsedInBlocks) == 1: + self.promoteSingleBlock(ai) + return + + loads = [i for i in v.used_by if isinstance(i, Load)] + stores = [i for i in v.used_by if isinstance(i, Store)] + + # Each store instruction can be removed (later). + # Instead of storing the value, we use it + # where the load would have been! + replMap = {} + for store in stores: + replMap[store] = store.value + + # for each load, track back what the defining store + # was. + for load in loads: + pass + + def onFunction(self, f): + for bb in f.BasicBlocks: + allocs = [i for i in bb.Instructions if isinstance(i, Alloc)] + for i in allocs: + if isAllocPromotable(i): + self.promote(i)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/optimize.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,19 @@ +from mem2reg import Mem2RegPromotor +from transform import CommonSubexpressionElimination, CleanPass +from transform import DeadCodeDeleter, ConstantFolder + +def optimize(ir): + return + cf = ConstantFolder() + cf.run(ir) + return + dcd = DeadCodeDeleter() + m2r = Mem2RegPromotor() + clr = CleanPass() + cse = CommonSubexpressionElimination() + dcd.run(ir) + clr.run(ir) + m2r.run(ir) + cse.run(ir) + cf.run(ir) + dcd.run(ir)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/transform.py Tue Dec 03 18:00:22 2013 +0100 @@ -0,0 +1,142 @@ +""" + Transformation to optimize IR-code +""" + +import logging +import ir +# Standard passes: + +class FunctionPass: + def __init__(self): + self.logger = logging.getLogger('optimize') + + def run(self, ir): + """ Main entry point for the pass """ + self.logger.info('Running pass {}'.format(type(self))) + ir.check() + self.prepare() + for f in ir.Functions: + self.onFunction(f) + ir.check() + + def onFunction(self, f): + """ Override this virtual method """ + raise NotImplementedError() + + def prepare(self): + pass + + +class BasicBlockPass(FunctionPass): + def onFunction(self, f): + for bb in f.Blocks: + self.onBasicBlock(bb) + + def onBasicBlock(self, bb): + """ Override this virtual method """ + raise NotImplementedError() + + +class InstructionPass(BasicBlockPass): + def onBasicBlock(self, bb): + for ins in iter(bb.Instructions): + self.onInstruction(ins) + + def onInstruction(self, ins): + """ Override this virtual method """ + raise NotImplementedError() + + +class BasePass(BasicBlockPass): + def onBasicBlock(self, bb): + pass + + +# Usefull transforms: +class ConstantFolder(BasePass): + def __init__(self): + super().__init__() + self.ops = {} + self.ops['+'] = lambda x, y: x + y + self.ops['-'] = lambda x, y: x - y + self.ops['*'] = lambda x, y: x * y + self.ops['<<'] = lambda x, y: x << y + + def postExpr(self, expr): + if type(i) is BinaryOperator and i.operation in self.ops.keys() and type(i.a) is Const and type(i.b) is Const: + vr = self.ops[i.operation](i.a.value, i.b.value) + return Const(vr) + else: + return expr + + +class DeadCodeDeleter(BasicBlockPass): + def onBasicBlock(self, bb): + def instructionUsed(ins): + if not type(ins) in [ImmLoad, BinaryOperator]: + return True + if len(ins.defs) == 0: + # In case this instruction does not define any + # variables, assume it is usefull. + return True + return any(d.Used for d in ins.defs) + + change = True + while change: + change = False + for i in bb.Instructions: + if instructionUsed(i): + continue + bb.removeInstruction(i) + change = True + + +class CommonSubexpressionElimination(BasicBlockPass): + def onBasicBlock(self, bb): + constMap = {} + to_remove = [] + for i in bb.Instructions: + if isinstance(i, ImmLoad): + if i.value in constMap: + t_new = constMap[i.value] + t_old = i.target + logging.debug('Replacing {} with {}'.format(t_old, t_new)) + t_old.replaceby(t_new) + to_remove.append(i) + else: + constMap[i.value] = i.target + elif isinstance(i, BinaryOperator): + k = (i.value1, i.operation, i.value2) + if k in constMap: + t_old = i.result + t_new = constMap[k] + logging.debug('Replacing {} with {}'.format(t_old, t_new)) + t_old.replaceby(t_new) + to_remove.append(i) + else: + constMap[k] = i.result + for i in to_remove: + logging.debug('removing {}'.format(i)) + bb.removeInstruction(i) + + +class CleanPass(FunctionPass): + def onFunction(self, f): + removeEmptyBasicBlocks(f) + + +def removeEmptyBlocks(f): + """ Remove empty basic blocks from function. """ + # If a block only contains a branch, it can be removed: + empty = lambda b: type(b.FirstInstruction) is ir.Jump + empty_blocks = list(filter(empty, f.Blocks)) + for b in empty_blocks: + # Update predecessors + preds = b.Predecessors + if b not in preds + [f.entry]: + # Do not remove if preceeded by itself + tgt = b.LastInstruction.target + for pred in preds: + pred.LastInstruction.changeTarget(b, tgt) + logging.debug('Removing empty block: {}'.format(b)) + f.removeBlock(b)
--- a/python/target/armframe.py Sun Dec 01 18:37:23 2013 +0100 +++ b/python/target/armframe.py Tue Dec 03 18:00:22 2013 +0100 @@ -1,7 +1,7 @@ import ir from .basetarget import Label, Comment, Alignment, LabelRef, DebugInfo, Nop from .basetarget import Imm7 -from irmach import AbstractInstruction as makeIns, Frame +from irmach import makeIns, Frame from .arminstructions import Dcd, AddSp, SubSp, Push, Pop, Mov2 from .arminstructions import R0, R1, R2, R3, R4, R5, R6, R7, LR, PC, SP
--- a/python/target/arminstructions.py Sun Dec 01 18:37:23 2013 +0100 +++ b/python/target/arminstructions.py Tue Dec 03 18:00:22 2013 +0100 @@ -418,14 +418,14 @@ return '{} {}, {}, {}'.format(self.mnemonic, self.rd, self.rn, self.imm3.imm) @instruction -class addregregimm3_ins(regregimm3_base): +class Add2(regregimm3_base): """ add Rd, Rn, imm3 """ mnemonic = 'add' opcode = 0b0001110 @instruction -class subregregimm3_ins(regregimm3_base): +class Sub2(regregimm3_base): """ sub Rd, Rn, imm3 """ mnemonic = 'sub' opcode = 0b0001111 @@ -491,7 +491,7 @@ @instruction -class mulregreg_ins(ArmInstruction): +class Mul(ArmInstruction): """ mul Rn, Rdm """ operands = (Reg8Op, Reg8Op) mnemonic = 'MUL'
--- a/python/target/arminstructionselector.py Sun Dec 01 18:37:23 2013 +0100 +++ b/python/target/arminstructionselector.py Tue Dec 03 18:00:22 2013 +0100 @@ -4,7 +4,7 @@ from .instructionselector import InstructionSelector from .arminstructions import Orr, Lsl, Str2, Ldr2, Ldr3, B, Bl, Bgt, Blt, Beq from .arminstructions import Mov2, Mov3 -from .arminstructions import Add, Sub, Cmp +from .arminstructions import Add, Sub, Cmp, Sub2, Add2, Mul from .basetarget import Imm8, Imm7, Imm3 @@ -18,7 +18,7 @@ a = self.munchExpr(e.a) d = self.newTmp() c = Imm3(e.b.value) - self.emit(arm.addregregimm3_ins, others=[c], dst=[d], src=[a]) + self.emit(Add2, others=[c], dst=[d], src=[a]) return d elif isinstance(e, ir.Binop) and e.operation == '+': a = self.munchExpr(e.a) @@ -31,7 +31,7 @@ a = self.munchExpr(e.a) d = self.newTmp() c = Imm3(e.b.value) - self.emit(arm.subregregimm3_ins, others=[c], dst=[d], src=[a]) + self.emit(Sub2, others=[c], dst=[d], src=[a]) return d elif isinstance(e, ir.Binop) and e.operation == '-': a = self.munchExpr(e.a) @@ -59,7 +59,7 @@ d = self.newTmp() self.move(d, a) # this mul instruction has operands swapped: - self.emit(arm.mulregreg_ins, dst=[d], src=[b, d]) + self.emit(Mul, dst=[d], src=[b, d]) return d elif isinstance(e, ir.Const) and e.value < 256: d = self.newTmp()
--- a/python/transform.py Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ -""" - Transformation to optimize IR-code -""" - -import logging -import ir -# Standard passes: - -class FunctionPass: - def __init__(self): - self.logger = logging.getLogger('optimize') - - def run(self, ir): - """ Main entry point for the pass """ - self.logger.info('Running pass {}'.format(type(self))) - ir.check() - self.prepare() - for f in ir.Functions: - self.onFunction(f) - ir.check() - - def onFunction(self, f): - """ Override this virtual method """ - raise NotImplementedError() - - def prepare(self): - pass - - -class BasicBlockPass(FunctionPass): - def onFunction(self, f): - for bb in f.Blocks: - self.onBasicBlock(bb) - - def onBasicBlock(self, bb): - """ Override this virtual method """ - raise NotImplementedError() - - -class InstructionPass(BasicBlockPass): - def onBasicBlock(self, bb): - for ins in iter(bb.Instructions): - self.onInstruction(ins) - - def onInstruction(self, ins): - """ Override this virtual method """ - raise NotImplementedError() - - -class BasePass(BasicBlockPass): - def onBasicBlock(self, bb): - pass - - -# Usefull transforms: -class ConstantFolder(BasePass): - def __init__(self): - super().__init__() - self.ops = {} - self.ops['+'] = lambda x, y: x + y - self.ops['-'] = lambda x, y: x - y - self.ops['*'] = lambda x, y: x * y - self.ops['<<'] = lambda x, y: x << y - - def postExpr(self, expr): - if type(i) is BinaryOperator and i.operation in self.ops.keys() and type(i.a) is Const and type(i.b) is Const: - vr = self.ops[i.operation](i.a.value, i.b.value) - return Const(vr) - else: - return expr - - -class DeadCodeDeleter(BasicBlockPass): - def onBasicBlock(self, bb): - def instructionUsed(ins): - if not type(ins) in [ImmLoad, BinaryOperator]: - return True - if len(ins.defs) == 0: - # In case this instruction does not define any - # variables, assume it is usefull. - return True - return any(d.Used for d in ins.defs) - - change = True - while change: - change = False - for i in bb.Instructions: - if instructionUsed(i): - continue - bb.removeInstruction(i) - change = True - - -class CommonSubexpressionElimination(BasicBlockPass): - def onBasicBlock(self, bb): - constMap = {} - to_remove = [] - for i in bb.Instructions: - if isinstance(i, ImmLoad): - if i.value in constMap: - t_new = constMap[i.value] - t_old = i.target - logging.debug('Replacing {} with {}'.format(t_old, t_new)) - t_old.replaceby(t_new) - to_remove.append(i) - else: - constMap[i.value] = i.target - elif isinstance(i, BinaryOperator): - k = (i.value1, i.operation, i.value2) - if k in constMap: - t_old = i.result - t_new = constMap[k] - logging.debug('Replacing {} with {}'.format(t_old, t_new)) - t_old.replaceby(t_new) - to_remove.append(i) - else: - constMap[k] = i.result - for i in to_remove: - logging.debug('removing {}'.format(i)) - bb.removeInstruction(i) - - -class CleanPass(FunctionPass): - def onFunction(self, f): - removeEmptyBasicBlocks(f) - - -def removeEmptyBlocks(f): - """ Remove empty basic blocks from function. """ - # If a block only contains a branch, it can be removed: - empty = lambda b: type(b.FirstInstruction) is ir.Jump - empty_blocks = list(filter(empty, f.Blocks)) - for b in empty_blocks: - # Update predecessors - preds = b.Predecessors - if b not in preds + [f.entry]: - # Do not remove if preceeded by itself - tgt = b.LastInstruction.target - for pred in preds: - pred.LastInstruction.changeTarget(b, tgt) - logging.debug('Removing empty block: {}'.format(b)) - f.removeBlock(b)
--- a/python/zcc.py Sun Dec 01 18:37:23 2013 +0100 +++ b/python/zcc.py Tue Dec 03 18:00:22 2013 +0100 @@ -4,7 +4,7 @@ import argparse import logging -import c3 +from ppci.c3 import Builder import ppci import codegen import outstream @@ -52,7 +52,7 @@ """ logging.info('Zcc started') # Front end: - c3b = c3.Builder(diag) + c3b = Builder(diag) cg = codegen.CodeGenerator(tg) # TODO: remove this arm specifics:
--- a/readme.rst Sun Dec 01 18:37:23 2013 +0100 +++ b/readme.rst Tue Dec 03 18:00:22 2013 +0100 @@ -18,7 +18,7 @@ Software dependencies --------------------- -* python3 +* python3.3 * (optional) pyqt5, pyqt4 or pyside How to start the IDE @@ -33,7 +33,7 @@ Docs ---- -Docs are here: +Docs are located here: http://lcfos.readthedocs.org/en/latest/ Run unittests @@ -42,12 +42,14 @@ .. code:: bash cd test - python -m unittest + ./runtests.sh Status ------ +The project is contains tests which are run continuously at drone.io. + .. image:: https://drone.io/bitbucket.org/windel/lcfos/status.png .. image:: https://www.ohloh.net/p/lcfos/widgets/project_thin_badge.gif
--- a/test/c3examples/blink.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* This file blinks a LED on the STM32F4 discovery board. - -the board has 4 leds on PD12, PD13, PD14 and PD15 - -*/ - -module blink; - -import stm32f4xx; - -// Functions: -function void main() -{ - // Memory mapped control registers: - var GPIO_Type GPIOD; - GPIOD = cast<GPIO_Type>(0x40020C00); - var RCC_Type RCC; - RCC = cast<RCC_Type>(0x40023800); - - // Enable the clock to port D: - RCC->AHB1ENR = RCC->AHB1ENR | 0x8; - - - // PD13 == output (01) - GPIOD->MODER = (1 << 26); - GPIOD->ODR = (1 << 13); - - while(true) {} -} -
--- a/test/c3examples/burn.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/* - -This file blinks a LED on the STM32F4 discovery board. - -the board has 4 leds on PD12, PD13, PD14 and PD15 - -*/ - -module burn; - -import stm32f4xx; - -/* -function void init() -{ -} -*/ - -function void main() -{ - //init(); - var RCC_Type RCC; - RCC = cast<RCC_Type>(0x40023800); - - // Enable the clock to port D: - RCC->AHB1ENR = RCC->AHB1ENR | (1 << 3); - // Memory mapped control registers: - var GPIO_Type GPIOD; - GPIOD = cast<GPIO_Type>(0x40020C00); - - var int pin; - pin = 15; - // PD13 == output (01) - GPIOD->MODER = (1 << (pin << 1)); - GPIOD->ODR = (1 << pin); - - while(true) {} -} -
--- a/test/c3examples/burn.c3proj Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - -[Burn] -srcs = burn2.c3 -imps = stm32f4xx.c3 -target = arm -
--- a/test/c3examples/burn2.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - -This file blinks a LED on the STM32F4 discovery board. - -the board has 4 leds on PD12, PD13, PD14 and PD15 - -*/ - -module burn2; - -import stm32f4xx; - -function int add(int a, int b) -{ - return a + b; -} - -function void init(int pin) -{ - if (pin < 12) - { - return; - } - - if (pin > 15) - { - return; - } - - var stm32f4xx:RCC_Type RCC; - RCC = cast<stm32f4xx:RCC_Type>(0x40023800); - - // Enable the clock to port D: - RCC->AHB1ENR = RCC->AHB1ENR | (1 << 3); - // Memory mapped control registers: - var stm32f4xx:GPIO_Type GPIOD; - GPIOD = cast<stm32f4xx:GPIO_Type>(0x40020C00); - - // PD13 == output (01) - GPIOD->MODER = (1 << (pin << 1)); - GPIOD->ODR = (1 << pin); -} - - -function void main() -{ - // Vary between 12 and 15: - init(13); - - var int a; - a = 0 - while (a < 1000) - { - a = add(a, 1); - } - - while(true) {} -} -
--- a/test/c3examples/cast.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ - -/* - Demo of how to type cast -*/ - -module castdemo; - -function int testcast() -{ - var int a; - var int* b; - a = 3; - - b = cast<int*>(a); - - return 0; -} -
--- a/test/c3examples/comments.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ - -/* - block - comment -*/ - -// fjd jjd- -module comments; // hjfkds - -function int tst() -{ - return 0; -} -
--- a/test/c3examples/functions.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ - -/* - Demo of function usage -*/ - -module functiondemo; - -function void main() -{ - var int a, b, c; - a = 3; - b = a; - a =3; - b = fib(a + 9); - sum(a, b); -} - -function int fib(int x) -{ - return fib(x - 1) * x; -} - -function int sum(int a, int b) -{ - return a + b; -} -
--- a/test/c3examples/stm32f4xx.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ - -module stm32f4xx; - -type struct { - int MODER; - int OTYPER; - int OSPEEDR; - int PUPDR; - int IDR; - int ODR; -}* GPIO_Type; - -type struct { - int CR; - int PLLCFGR; - int CFGR; - int CIR; - int AHB1RSTR; - int AHB2RSTR; - int AHB3RSTR; - int reserved0; - int APB1RSTR; - int APB2RSTR; - int reserved1a, reserved1b; - int AHB1ENR; - int AHB2ENR; - int AHB3ENR; - int reserved2; - int APB1ENR, APB2ENR; -}* RCC_Type; -
--- a/test/c3examples/types.c3 Sun Dec 01 18:37:23 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ - -/* - Demo of how to define types -*/ - -module typedemo; - -type int A; -type int B; -type struct { - int x, y; - A z; -} C; -type struct { - C x; - B y; - int z; -} D; - -type D* E; - -function int testcast() -{ - var A a; - var B b; - a = 3; - b = a; - var C c; - c.x = a; - c.z = c.y; - var D d; - var E e; - var D* e2; - e = &d; - e2 = e; - e2->x.x = 22; - - return 0; -} -
--- a/test/runtests.sh Sun Dec 01 18:37:23 2013 +0100 +++ b/test/runtests.sh Tue Dec 03 18:00:22 2013 +0100 @@ -14,7 +14,7 @@ done else set -e - python -m unittest -v + python -m unittest fi
--- a/test/testc3.py Sun Dec 01 18:37:23 2013 +0100 +++ b/test/testc3.py Tue Dec 03 18:00:22 2013 +0100 @@ -1,74 +1,13 @@ -import c3 -import time +from ppci.c3 import Builder, Lexer import ppci -import ir import unittest -import glob import io -testsrc = """module test; - -/* - demo of the source that is correct :) -*/ -var int c, d; -var double e; -var int f; - -const int A = 1337; - -function void test1() -{ - var int bdd; - var int a = 10; - bdd = 20; - var int buf; - var int i; - i = 2; - var int zero = i - 2; - if (i > 1) - { - buf = b + 22 * i - 13 + (55 * 2 *9-2) / 44 - 1; - } - else - { - ;;; - } - - t2(2, 3); -} - -function int t2(int a, int b) -{ - if (a > 0) - { - a = 2 + t2(a - 1, 10); - } - - return a + b; -} - -var int a, b; - -function int t3(int aap, int blah) -{ - if (a > blah and blah < 45 + 33 or 33 > aap or 6 > 2 and true) - { - a = 2 + t2(a - 1, 0); - } - - return a + b; -} - -var int hahaa = 23 * 2; - - -""" class testLexer(unittest.TestCase): def setUp(self): diag = ppci.DiagnosticsManager() - self.l = c3.Lexer(diag) + self.l = Lexer(diag) def testUnexpectedCharacter(self): snippet = io.StringIO(""" var s \u6c34 """) @@ -102,12 +41,9 @@ class testBuilder(unittest.TestCase): def setUp(self): self.diag = ppci.DiagnosticsManager() - self.builder = c3.Builder(self.diag) + self.builder = Builder(self.diag) self.diag.clear() - def testSrc(self): - self.expectOK(testsrc) - def expectErrors(self, snippet, rows): """ Helper to test for expected errors on rows """ ircode = list(self.builder.build([io.StringIO(snippet)])) @@ -309,7 +245,6 @@ """ self.expectOK(snippet) - @unittest.skip('Too strange to handle') def testStructCall(self): snippet = """ module teststruct1; @@ -437,41 +372,6 @@ """ self.expectOK(snippet) - @unittest.skip('To be rewritten') - def testExamples(self): - """ Test all examples in the c3/examples directory """ - example_filenames = glob.glob('./c3examples/*.c3') - for filename in example_filenames: - with open(filename, 'r') as f: - self.expectOK(f) - - def test2(self): - # testsrc2 is valid code: - snippet = """ - module test2; - - function void tst() - { - var int a, b; - a = 2 * 33 - 12; - b = a * 2 + 13; - a = b + a; - if (a > b and b == 3) - { - var int x = a; - x = b * 2 - a; - a = x*x; - } - else - { - a = b + a; - } - } - - """ - self.expectOK(snippet) if __name__ == '__main__': unittest.main() - -
--- a/test/testir.py Sun Dec 01 18:37:23 2013 +0100 +++ b/test/testir.py Tue Dec 03 18:00:22 2013 +0100 @@ -1,10 +1,8 @@ import unittest, os import sys -import c3 import ppci import ir -import transform -import optimize +from ppci.transform import ConstantFolder class IrCodeTestCase(unittest.TestCase): @@ -53,7 +51,7 @@ class ConstantFolderTestCase(unittest.TestCase): def setUp(self): self.b = ir.Builder() - self.cf = transform.ConstantFolder() + self.cf = ConstantFolder() self.m = ir.Module('test') self.b.setModule(self.m)
--- a/test/testzcc.py Sun Dec 01 18:37:23 2013 +0100 +++ b/test/testzcc.py Tue Dec 03 18:00:22 2013 +0100 @@ -11,7 +11,7 @@ """ Tests the compiler driver """ def do(self, filenames, imps=[]): - basedir = 'c3examples' + basedir = os.path.join('..', 'examples', 'c3') arg_list = [os.path.join(basedir, fn) for fn in filenames] for fn in imps: arg_list.append('-i') @@ -21,10 +21,6 @@ args = zcc.parser.parse_args(arg_list) self.assertEqual(0, zcc.main(args)) - @unittest.skip('Not working yet') - def testBurn(self): - self.do(['burn.c3'], ['stm32f4xx.c3']) - def testBurn2(self): self.do(['burn2.c3'], ['stm32f4xx.c3']) @@ -34,6 +30,9 @@ def testCast(self): self.do(['cast.c3']) + def testFunctions(self): + self.do(['functions.c3']) + def testSectionAddress(self): src = """module tst; function void t2() {var int t3; t3 = 2;} @@ -51,4 +50,3 @@ if __name__ == '__main__': unittest.main() -