# HG changeset patch
# User windel
# Date 1349621810 -7200
# Node ID fd7d5069734e26e4dbeab714bbc6238981a95359
# Parent 6fa41208a3a886d268e41b708244c5a6349e69fc
Rename application to python
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/__init__.py
--- a/applications/ide/compiler/__init__.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-# File to make this directory a package.
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/assembler.py
--- a/applications/ide/compiler/assembler.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,354 +0,0 @@
-"""
- Assembler code generation functions
-"""
-
-from .errors import Error
-
-modrm = {'rax': 0, 'rbx': 1}
-
-# Table 3.1 of the intel manual:
-# use REX.W on the table below:
-regs64 = {'rax': 0,'rcx':1,'rdx':2,'rbx':3,'rsp':4,'rbp':5,'rsi':6,'rdi':7,'r8':0,'r9':1,'r10':2,'r11':3,'r12':4,'r13':5,'r14':6,'r15':7}
-regs32 = {'eax': 0, 'ecx':1, 'edx':2, 'ebx': 3, 'esp': 4, 'ebp': 5, 'esi':6, 'edi':7}
-regs8 = {'al':0,'cl':1,'dl':2,'bl':3,'ah':4,'ch':5,'dh':6,'bh':7}
-
-# Calculation of the rexb bit:
-rexbit = {'rax': 0, 'rcx':0, 'rdx':0, 'rbx': 0, 'rsp': 0, 'rbp': 0, 'rsi':0, 'rdi':0,'r8':1,'r9':1,'r10':1,'r11':1,'r12':1,'r13':1,'r14':1,'r15':1}
-
-# Helper functions:
-def imm64(x):
- """ represent 64 bits integer in little endian 8 bytes"""
- if x < 0:
- x = x + (1 << 64)
- x = x & 0xFFFFFFFFFFFFFFFF
- return [ (x >> (p*8)) & 0xFF for p in range(8) ]
-
-def imm32(x):
- """ represent 32 bits integer in little endian 4 bytes"""
- if x < 0:
- x = x + (1 << 32)
- x = x & 0xFFFFFFFF
- return [ (x >> (p*8)) & 0xFF for p in range(4) ]
-
-def imm8(x):
- if x < 0:
- x = x + (1 << 8)
- x = x & 0xFF
- return [ x ]
-
-def modrm(mod=0, rm=0, reg=0):
- """ Construct the modrm byte from its components """
- assert(mod <= 3)
- assert(rm <= 7)
- assert(reg <= 7)
- return (mod << 6) | (reg << 3) | rm
-
-def rex(w=0, r=0, x=0, b=0):
- """ Create a REX prefix byte """
- assert(w <= 1)
- assert(r <= 1)
- assert(x <= 1)
- assert(b <= 1)
- return 0x40 | (w<<3) | (r<<2) | (x<<1) | b
-
-def sib(ss=0, index=0, base=0):
- assert(ss <= 3)
- assert(index <= 7)
- assert(base <= 7)
- return (ss << 6) | (index << 3) | base
-
-tttn = {'L':0xc,'G':0xf,'NE':0x5,'GE':0xd,'LE':0xe, 'E':0x4}
-
-# Actual instructions:
-def nearjump(distance, condition=None):
- """ jmp imm32 """
- lim = (1<<30)
- if abs(distance) > lim:
- Error('near jump cannot jump over more than {0} bytes'.format(lim))
- if condition:
- if distance < 0:
- distance -= 6 # Skip own instruction
- opcode = 0x80 | tttn[condition] # Jcc imm32
- return [0x0F, opcode] + imm32(distance)
- else:
- if distance < 0:
- distance -= 5 # Skip own instruction
- return [ 0xE9 ] + imm32(distance)
-
-def shortjump(distance, condition=None):
- """ jmp imm8 """
- lim = 118
- if abs(distance) > lim:
- Error('short jump cannot jump over more than {0} bytes'.format(lim))
- if distance < 0:
- distance -= 2 # Skip own instruction
- if condition:
- opcode = 0x70 | tttn[condition] # Jcc rel8
- else:
- opcode = 0xeb # jmp rel8
- return [opcode] + imm8(distance)
-
-# Helper that determines jump type:
-def reljump(distance):
- if abs(distance) < 110:
- return shortjump(distance)
- else:
- return nearjump(distance)
-
-def push(reg):
- if reg in regs64:
- if rexbit[reg] == 1:
- return [0x41, 0x50 + regs64[reg]]
- else:
- return [0x50 + regs64[reg]]
- else:
- Error('push for {0} not implemented'.format(reg))
-
-def pop(reg):
- if reg in regs64:
- if rexbit[reg] == 1:
- rexprefix = rex(b=1)
- opcode = 0x58 + regs64[reg]
- return [rexprefix, opcode]
- else:
- opcode = 0x58 + regs64[reg]
- return [ opcode ]
- else:
- Error('pop for {0} not implemented'.format(reg))
-
-def INT(number):
- opcode = 0xcd
- return [opcode] + imm8(number)
-
-def syscall():
- return [0x0F, 0x05]
-
-def call(distance):
- if type(distance) is int:
- return [0xe8]+imm32(distance)
- elif type(distance) is str and distance in regs64:
- reg = distance
- opcode = 0xFF # 0xFF /2 == call r/m64
- mod_rm = modrm(mod=3, reg=2, rm=regs64[reg])
- if rexbit[reg] == 1:
- rexprefix = rex(b=rexbit[reg])
- return [rexprefix, opcode, mod_rm]
- else:
- return [opcode, mod_rm]
- else:
- Error('Cannot call to {0}'.format(distance))
-
-def ret():
- return [ 0xc3 ]
-
-def increg64(reg):
- assert(reg in regs64)
- rexprefix = rex(w=1, b=rexbit[reg])
- opcode = 0xff
- mod_rm = modrm(mod=3, rm=regs64[reg])
- return [rexprefix, opcode, mod_rm]
-
-def prepost8(r8, rm8):
- assert(r8 in regs8)
- pre = []
- if type(rm8) is list:
- # TODO: merge mem access with prepost for 64 bits
- if len(rm8) == 1:
- base, = rm8
- if type(base) is str and base in regs64:
- assert(not base in ['rbp', 'rsp', 'r12', 'r13'])
- mod_rm = modrm(mod=0, rm=regs64[base], reg=regs8[r8])
- if rexbit[base] == 1:
- pre.append(rex(b=1))
- post = [mod_rm]
- else:
- Error('One arg of type {0} not implemented'.format(base))
- elif len(rm8) == 2:
- base, offset = rm8
- assert(type(offset) is int)
- assert(base in regs64)
-
- if base == 'rsp' or base == 'r12':
- Error('Cannot use rsp or r12 as base yet')
- if rexbit[base] == 1:
- pre.append( rex(b=1) )
- mod_rm = modrm(mod=1, rm=regs64[base], reg=regs8[r8])
- post = [mod_rm] + imm8(offset)
- else:
- Error('not supporting prepost8 with list len {0}'.format(len(rm8)))
- else:
- Error('Not supporting move with reg8 {0}'.format(r8))
- return pre, post
-
-def prepost(r64, rm64):
- assert(r64 in regs64)
- if type(rm64) is list:
- if len(rm64) == 3:
- base, index, disp = rm64
- assert(base in regs64)
- assert(index in regs64)
- assert(type(disp) is int)
- # Assert that no special cases are used:
- # TODO: swap base and index to avoid special cases
- # TODO: exploit special cases and make better code
- assert(index != 'rsp')
-
- rexprefix = rex(w=1, r=rexbit[r64], x=rexbit[index], b=rexbit[base])
- # mod=1 and rm=4 indicates a SIB byte: [--][--]+imm8
- mod_rm = modrm(mod=1, rm=4, reg=regs64[r64])
- si_b = sib(ss=0, index=regs64[index], base=regs64[base])
- return [rexprefix], [mod_rm, si_b] + imm8(disp)
- elif len(rm64) == 2:
- base, offset = rm64
- assert(type(offset) is int)
- if base == 'RIP':
- # RIP pointer relative addressing mode!
- rexprefix = rex(w=1, r=rexbit[r64])
- mod_rm = modrm(mod=0, rm=5, reg=regs64[r64])
- return [rexprefix], [mod_rm] + imm32(offset)
- else:
- assert(base in regs64)
-
- if base == 'rsp' or base == 'r12':
- # extended function that uses SIB byte
- rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base])
- # rm=4 indicates a SIB byte follows
- mod_rm = modrm(mod=1, rm=4, reg=regs64[r64])
- # index=4 indicates that index is not used
- si_b = sib(ss=0, index=4, base=regs64[base])
- return [rexprefix], [mod_rm, si_b] + imm8(offset)
- else:
- rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base])
- mod_rm = modrm(mod=1, rm=regs64[base], reg=regs64[r64])
- return [rexprefix], [mod_rm] + imm8(offset)
- elif len(rm64) == 1:
- offset = rm64[0]
- if type(offset) is int:
- rexprefix = rex(w=1, r=rexbit[r64])
- mod_rm = modrm(mod=0, rm=4,reg=regs64[r64])
- si_b = sib(ss=0, index=4,base=5) # 0x25
- return [rexprefix], [mod_rm, si_b] + imm32(offset)
- else:
- Error('Memory reference of type {0} not implemented'.format(offset))
- else:
- Error('Memory reference not implemented')
- elif rm64 in regs64:
- rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[rm64])
- mod_rm = modrm(3, rm=regs64[rm64], reg=regs64[r64])
- return [rexprefix], [mod_rm]
-
-def leareg64(rega, m):
- opcode = 0x8d # lea r64, m
- pre, post = prepost(rega, m)
- return pre + [opcode] + post
-
-def mov(rega, regb):
- if type(regb) is int:
- pre = [rex(w=1, b=rexbit[rega])]
- opcode = 0xb8 + regs64[rega]
- post = imm64(regb)
- elif type(regb) is str:
- if regb in regs64:
- opcode = 0x89 # mov r/m64, r64
- pre, post = prepost(regb, rega)
- elif regb in regs8:
- opcode = 0x88 # mov r/m8, r8
- pre, post = prepost8(regb, rega)
- else:
- Error('Unknown register {0}'.format(regb))
- elif type(rega) is str:
- if rega in regs64:
- opcode = 0x8b # mov r64, r/m64
- pre, post = prepost(rega, regb)
- else:
- Error('Unknown register {0}'.format(rega))
- else:
- Error('Move of this kind {0}, {1} not implemented'.format(rega, regb))
- return pre + [opcode] + post
-
-def xorreg64(rega, regb):
- rexprefix = rex(w=1, r=rexbit[regb], b=rexbit[rega])
- opcode = 0x31 # XOR r/m64, r64
- # Alternative is 0x33 XOR r64, r/m64
- mod_rm = modrm(3, rm=regs64[rega], reg=regs64[regb])
- return [rexprefix, opcode, mod_rm]
-
-# integer arithmatic:
-def addreg64(rega, regb):
- if regb in regs64:
- pre, post = prepost(regb, rega)
- opcode = 0x01 # ADD r/m64, r64
- return pre + [opcode] + post
- elif type(regb) is int:
- if regb < 100:
- rexprefix = rex(w=1, b=rexbit[rega])
- opcode = 0x83 # add r/m, imm8
- mod_rm = modrm(3, rm=regs64[rega], reg=0)
- return [rexprefix, opcode, mod_rm]+imm8(regb)
- elif regb < (1<<31):
- rexprefix = rex(w=1, b=rexbit[rega])
- opcode = 0x81 # add r/m64, imm32
- mod_rm = modrm(3, rm=regs64[rega], reg=0)
- return [rexprefix, opcode, mod_rm]+imm32(regb)
- else:
- Error('Constant value too large!')
- else:
- Error('unknown second operand!'.format(regb))
-
-def subreg64(rega, regb):
- if regb in regs64:
- pre, post = prepost(regb, rega)
- opcode = 0x29 # SUB r/m64, r64
- return pre + [opcode] + post
- elif type(regb) is int:
- if regb < 100:
- rexprefix = rex(w=1, b=rexbit[rega])
- opcode = 0x83 # sub r/m, imm8
- mod_rm = modrm(3, rm=regs64[rega], reg=5)
- return [rexprefix, opcode, mod_rm]+imm8(regb)
- elif regb < (1<<31):
- rexprefix = rex(w=1, b=rexbit[rega])
- opcode = 0x81 # sub r/m64, imm32
- mod_rm = modrm(3, rm=regs64[rega], reg=5)
- return [rexprefix, opcode, mod_rm]+imm32(regb)
- else:
- Error('Constant value too large!')
-
- else:
- Error('unknown second operand!'.format(regb))
-
-def idivreg64(reg):
- rexprefix = rex(w=1, b=rexbit[reg])
- opcode = 0xf7 # IDIV r/m64
- mod_rm = modrm(3, rm=regs64[reg], reg=7)
- return [rexprefix, opcode, mod_rm]
-
-def imulreg64_rax(reg):
- rexprefix = rex(w=1, b=rexbit[reg])
- opcode = 0xf7 # IMUL r/m64
- mod_rm = modrm(3, rm=regs64[reg], reg=5)
- return [rexprefix, opcode, mod_rm]
-
-def imulreg64(rega, regb):
- pre, post = prepost(rega, regb)
- opcode = 0x0f # IMUL r64, r/m64
- opcode2 = 0xaf
- return pre + [opcode, opcode2] + post
-
-def cmpreg64(rega, regb):
- if regb in regs64:
- pre, post = prepost(regb, rega)
- opcode = 0x39 # CMP r/m64, r64
- return pre + [opcode] + post
- elif type(regb) is int:
- rexprefix = rex(w=1, b=rexbit[rega])
- opcode = 0x83 # CMP r/m64, imm8
- mod_rm = modrm(3, rm=regs64[rega], reg=7)
- return [rexprefix, opcode, mod_rm] + imm8(regb)
-
- else:
- Error('not implemented cmp64')
-
-# Mapping that maps string names to the right functions:
-opcodes = {'mov':(mov,2), 'lea':(leareg64,2), 'int':(INT,1), 'syscall':(syscall,0)}
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/builtin.py
--- a/applications/ide/compiler/builtin.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-from .nodes import *
-
-boolean = BaseType('boolean', 8) # Choose: 1 or 8 bytes?
-integer = BaseType('integer', 8)
-real = BaseType('real', 8)
-char = BaseType('char', 1)
-void = BaseType('void', 0)
-
-chr_func = BuiltinProcedure('chr', ProcedureType([Parameter('value', 'x', integer)], char))
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/codegenerator.py
--- a/applications/ide/compiler/codegenerator.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,487 +0,0 @@
-"""
- Code generation for 64 bits intel processors
-"""
-
-from .nodes import *
-from .errors import Error
-from .builtin import real, integer, boolean, char
-from .assembler import *
-
-class CodeGenerator:
- def __init__(self):
- self.strings = []
- self.initialize()
- def initialize(self):
- # Register descriptors:
- self.freeregs = 'r8,r9,r10,r11,r12,r13,r14,r15'.split(',')
- self.usedregs = []
- # Members to accumulate the result into:
- # The result is an image of bytecode and global variable space.
- # Global variables a referenced by RIP relative addressing.
- self.image = []
- self.rip = 0 # The current instruction pointer location.
- # TODO: backpatch list here?
-
- # Functions to modify the code image
- def addCode(self, code):
- assert(type(code) is list)
- self.image += code
- self.rip += len(code)
- def fixCode(self, position, code):
- self.image[position:position+len(code)] = code
- def align(self, b):
- while (self.rip % b) != 0:
- self.addCode([0])
-
- def saveAllRegisters(self):
- regs = list(self.usedregs.keys())
- for reg in regs:
- code += self.saveRegister(reg)
-
- def saveRegister(self, reg):
- code = []
- if reg in self.usedregs.keys():
- code.append('mov {0}, {1}'.format(self.usedregs[reg], reg))
- del self.usedregs[reg]
- self.freeregs.append(reg)
-
- def getreg(self, node):
- """ acquire a working register for a certain node."""
- # Temporary register bypass action:
- if len(self.freeregs) > 0:
- reg = self.freeregs.pop(0)
- self.usedregs.append(reg)
- else:
- Error('No more free regs')
- node.reg = reg
-
- def freereg(self, node):
- reg = node.reg
- node.reg = None
- self.freeregs.append(reg)
- self.usedregs.remove(reg)
-
- # Helpers to load and retrieve designated objects:
- def storeRegInDesignator(self, reg, designator):
- assert(type(reg) is str)
- assert(type(designator) is Designator)
- if len(designator.selectors) > 0:
- self.gencode( designator ) # Load the pointer into some register
- self.addCode( mov([designator.reg, 0x0], reg) )
- self.freereg( designator )
- else:
- if designator.obj.isLocal:
- # Relative from rbp register
- mem = ['rbp', designator.obj.offset]
- self.addCode( mov(mem, reg) )
- else:
- # Relative from RIP after move
- self.addCode( mov(['RIP', 0x0], reg) )
- self.fixCode(self.rip - 4, imm32(designator.obj.offset - self.rip) )
-
- # Code generation functions:
- def genexprcode(self, node):
- """
- Generate code for expressions!
- Recursively evaluates, and ensures a register contains the answer.
- register is an integer register or a floating point reg
- """
- if isinstance(node, Binop):
- """ Handle a binary operation (two arguments) of some kind """
- self.genexprcode(node.a)
- self.genexprcode(node.b)
-
- if node.op == 'mod':
- assert(node.typ.isType(integer))
- self.addCode(mov('rax', node.a.reg))
- self.addCode(xorreg64('rdx', 'rdx')) # Extend divided number with zeros
- self.addCode(idivreg64(node.b.reg)) # divide rdx:rax with reg
- node.reg = node.a.reg
- self.freereg(node.b) # give up register that contains b
- self.addCode(mov(node.reg, 'rdx')) # move remainder into result
- elif node.op == 'div':
- assert(node.typ.isType(integer))
- self.addCode(mov('rax', node.a.reg))
- self.addCode(xorreg64('rdx', 'rdx')) # Extend divided number with zeros
- self.addCode(idivreg64(node.b.reg)) # divide rdx:rax with reg
- node.reg = node.a.reg
- self.freereg(node.b) # give up register that contains b
- self.addCode(mov(node.reg, 'rax')) # move result into reg
- elif node.op == '*':
- if node.typ.isType(integer):
- self.addCode(imulreg64(node.a.reg, node.b.reg))
- node.reg = node.a.reg
- self.freereg(node.b)
- else:
- Error('{0} for * not implemented'.format(node.typ))
- elif node.op == '+':
- if node.typ.isType(integer):
- self.addCode(addreg64(node.a.reg, node.b.reg))
- node.reg = node.a.reg
- self.freereg(node.b)
- else:
- Error('{0} for + not implemented'.format(node.typ))
- elif node.op == '-':
- if node.typ.isType(integer):
- self.addCode(subreg64(node.a.reg, node.b.reg))
- node.reg = node.a.reg
- self.freereg(node.b)
- else:
- Error('{0} for - not implemented'.format(node.typ))
- else:
- Error('Unknown Binop {0}'.format(node.op))
-
- elif type(node) is Unop:
- if node.op == 'INTTOREAL':
- self.genexprcode(node.a)
- node.reg = node.a.reg
- # TODO use 'FILD' instruction
- freg = 12
- code.append('Unop inttoreal TODO')
- elif node.op == 'ABS':
- if isType(node.typ, real):
- code = [0xD9, 0xE1] # st(0) = fabs st(0)
- Error('ABS error integer')
- elif isType(node.typ, integer):
- code = []
- Error('ABS error integer')
- else:
- Error('ABS error')
- else:
- Error('Unknown Unop {0}'.format(node.op))
-
- elif isinstance(node, Designator):
- # dereference, array index. Make sure that the result comes into a register
- if len(node.selectors) > 0:
- self.gencode(node) # Load the pointer into some register
-
- # Now we can access the object at location '[node.reg]':
- if node.typ.isType(integer):
- self.addCode( mov(node.reg, [node.reg, 0x0]) )
- else:
- Error('Only integer types implemented')
- else:
- # No selectors, load variable directly
- if node.obj.typ.isType(integer):
- if type(node.obj) is Constant:
- self.genexprcode(node.obj)
- node.reg = node.obj.reg
- else:
- self.getreg(node)
- # Get a register to store the integer value
- if node.obj.isLocal:
- # relative to rbp:
- self.addCode( mov(node.reg, ['rbp', node.obj.offset]) )
- else:
- self.addCode(mov(node.reg, ['RIP', 0x0]))
- self.fixCode(self.rip-4, imm32(node.obj.offset - self.rip))
- else:
- Error('Cannot load variable type {0}'.format(node.typ))
-
- elif isinstance(node, Relop):
- # Create a boolean from operands
- # TODO create an alternative for expressions used as conditions.
- self.genexprcode(node.a)
- self.genexprcode(node.b)
-
- if node.a.typ.isType(integer):
- instructions = {'<': 'L', '>': 'G', '<>': 'NE', '>=': 'GE', '<=': 'LE', '=':'E'}
- if not node.relop in instructions.keys():
- Error('Unimplemented relop: '+str(node.relop))
- instr = instructions[node.relop]
-
- node.reg = node.a.reg
- self.addCode( cmpreg64(node.a.reg, node.b.reg) )
- self.addCode( shortjump(0x0, condition=instr) ) # jump over 0 code and jmp
- fixloc1 = self.rip - 1
- rip1 = self.rip
- self.addCode( xorreg64(node.reg, node.reg) )
- self.addCode( shortjump(0x0) ) # Jump over 1 code
- fixloc2 = self.rip - 1
- self.fixCode(fixloc1, imm8(self.rip - rip1))
- rip2 = self.rip
- self.addCode( xorreg64(node.reg, node.reg) )
- self.addCode( increg64(node.reg) )
- self.fixCode(fixloc2, imm8(self.rip - rip2))
-
- self.freereg(node.b)
- else:
- Error('Relop not implemented for {0}'.format(node.a.typ))
-
- elif type(node) is Constant:
- if node.typ.isType(integer):
- self.getreg(node)
- self.addCode(mov(node.reg, node.value))
- elif node.typ.isType(real):
- code += self.getreg(node)
- Error('TODO: get real reg')
- # TODO: get a fixed point reg, and load the variable in there
- else:
- Error('Howto generate code for {0}?'.format(node))
-
- elif type(node) is ProcedureCall:
- if type(node.proc.obj) is BuiltinProcedure:
- # Handle builtin procedures different, these not always call
- # a function, but generate code.
- bi = node.proc.obj
- if bi.name == 'chr':
- arg = node.args[0]
- self.genexprcode(arg)
- # Store character in full width register:
- # TODO: store in char only register
- node.reg = arg.reg
- else:
- Error('Unknown builtin function {0}'.format(bi.name))
- else:
- # Use generic procedure call first
- self.gencode(node)
- # Retrieve result:
- if node.typ.isType(integer):
- # Store result!
- self.getreg(node)
- self.addCode( mov(node.reg, 'rax') )
- else:
- Error('Return type not supported {0}'.format(node.typ))
- else:
- Error('Cannot generate expression code for: {0}'.format(node))
-
- def gencode(self, node):
- """ Code generation function for AST nodes """
- if isinstance(node, Module):
- # for all imports make a list of pointer to the actual procedures:
- for imp in node.imports:
- imp.offset = self.rip
- self.addCode( [0x0]*8 )
- # global variable storage allocation
- variables = node.symtable.getAllLocal(Variable)
- for var in variables:
- var.isLocal = False
- var.offset = self.rip
- self.addCode( [0x00] * var.typ.size ) # TODO initial values here?
- self.align(8)
- # TODO: mark end of data and start of code inside image
- # TODO: round data to page size to enable protection by loader.
- # Procedure code generation:
- procedures = node.symtable.getAllLocal(Procedure)
- node.procs = procedures
- for proc in procedures:
- self.gencode(proc)
- # Module init code:
- node.initcodeentry = self.rip
- self.gencode(node.initcode)
- self.addCode( ret() )
- # TODO: how to return from module init code? far return??
-
- elif type(node) is Procedure:
- # calculate offsets for local variables and parameters
- # Variable location relative to 'rbp' register
- variables = node.symtable.getAllLocal(Variable)
- offset = 0
- paramoffset = 16
- for var in variables:
- var.isLocal = True
- if not var.isParameter:
- offset += var.typ.size
- # Offset is negative of rbp in stack frame
- var.offset = -offset
- node.framesize = offset
- # Calculate offsets of parameters relative to rbp register
- for par in reversed(node.typ.parameters):
- pvar = node.symtable.getLocal(Variable, par.name)
- pvar.offset = paramoffset
- paramoffset += pvar.typ.size
-
- # code generation
- node.entrypoint = self.rip
- self.addCode(push('rbp'))
- self.addCode(mov('rbp', 'rsp')) # Setup the base pointer
- self.addCode(subreg64('rsp', node.framesize)) # reserve space for locals
- self.gencode(node.block)
- if node.retexpr:
- if node.retexpr.typ.isType(integer):
- self.genexprcode(node.retexpr)
- self.addCode( mov('rax', node.retexpr.reg) )
- self.freereg(node.retexpr)
- else:
- Error('Cannot return this kind yet {0}'.format(node.retexpr.typ))
- self.addCode( addreg64('rsp', node.framesize) )
- self.addCode( pop('rbp') )
- self.addCode( ret() )
- assert(len(self.usedregs) == 0)
-
- elif isinstance(node, StatementSequence):
- for s in node.statements:
- self.gencode(s)
-
- elif type(node) is ProcedureCall:
- # Prepare parameters on the stack:
- stacksize = 0
- assert(len(node.args) == len(node.proc.typ.parameters))
- for arg, param in zip(node.args, node.proc.typ.parameters):
-
- if param.kind == 'value':
- self.genexprcode(arg)
- self.addCode( push(arg.reg) )
- self.freereg( arg )
- stacksize += 8
- else:
- Error('Parameter kind other than value')
-
- # Calculate address using designator
- if type(node.proc.obj) is Procedure:
- self.addCode( call(0x0) )
- self.fixCode( self.rip - 4, imm32(node.proc.obj.entrypoint - self.rip))
- elif type(node.proc.obj) is ImportedSymbol:
- # Load the entry point of the import table
- self.getreg(node.proc.obj)
- # Load the address of the procedure:
- self.addCode( mov(node.proc.obj.reg, ['RIP', 0x0]) )
- self.fixCode( self.rip - 4, imm32(node.proc.obj.offset - self.rip) )
- # Call to the address in register:
- self.addCode( call(node.proc.obj.reg) )
- # Free register that holds the address of the object
- self.freereg( node.proc.obj )
- elif type(node.proc.obj) is BuiltinProcedure:
- if node.proc.obj.name == 'chr':
- print('int to char')
- else:
- Error('Unknown builtin function {0}'.format(node.proc.obj.name))
- else:
- Error('Cannot call designator of type {0}'.format(node.proc.obj))
-
- # Restore stack (pop all arguments of):
- self.addCode(addreg64('rsp', stacksize))
-
- elif type(node) is Assignment:
- if node.lval.typ.isType(integer):
- # TODO if node.rval is Constant of some datatype, move it to mem directly
- self.genexprcode(node.rval) # Calculate the value that has to be stored.
- self.storeRegInDesignator(node.rval.reg, node.lval)
- self.freereg(node.rval)
- else:
- Error('Assignments of other types not implemented')
- # TODO if left and right are designators, do some sort of memcpy.
-
- elif type(node) is IfStatement:
- self.genexprcode(node.condition)
- self.addCode( cmpreg64(node.condition.reg, 1) )
- self.freereg(node.condition)
- if node.falsestatement:
- # If with else clause
- self.addCode( nearjump(0x0, condition='NE') ) # if Not Equal jump to false
- rip1 = self.rip
- fixloc1 = self.rip - 4
- self.gencode(node.truestatement)
- self.addCode( nearjump( 0x0 ) ) # jump over false code
- fixloc2 = self.rip - 4
- self.fixCode(fixloc1, imm32(self.rip - rip1))
- rip2 = self.rip
- self.gencode(node.falsestatement)
- self.fixCode(fixloc2, imm32(self.rip - rip2))
- else:
- # If without else clause
- self.addCode( nearjump(0x0, condition='NE') ) # if Not Equal jump to false
- rip1 = self.rip
- fixloc1 = self.rip - 4
- self.gencode(node.truestatement)
- self.fixCode(fixloc1, imm32(self.rip - rip1)) # Fixup near jump over true code.
-
- elif isinstance(node, WhileStatement):
- rip1 = self.rip # Store the start of the while loop
- self.genexprcode(node.condition)
- self.addCode( cmpreg64(node.condition.reg, 1) ) # Test condition for true-ness
- self.freereg(node.condition)
- self.addCode( nearjump(0x0, condition='NE') ) # If Not Equal jump over while code AND jump back (fix later)
- fixloc1 = self.rip - 4
- rip2 = self.rip
- self.gencode(node.dostatements)
- self.addCode( nearjump(0x0) ) # JMP to condition, fix exact jump position below
- fixloc2 = self.rip - 4
- rip3 = self.rip # end of while loop
- self.fixCode(fixloc2, imm32(rip1 - rip3)) # Fixup jump to start of while loop
- self.fixCode(fixloc1, imm32(rip3 - rip2)) # Fixup jump out of while loop
-
- elif type(node) is ForStatement:
- # Initial load of iterator variable:
- self.genexprcode(node.begin)
- self.genexprcode(node.end)
- # TODO: link reg with variable so that a register is used instead of a variable
- iterreg = node.begin.reg # Get the register used for the loop
- #self.addCode(cmpreg64(iterreg, node.endvalue))
- rip1 = self.rip
- self.gencode(node.statements)
- #self.loadDesignatorInReg(node.
- #self.addCode( addreg64(node.variable, node.increment) )
- self.addCode(nearjump(0x0))
- fixloc1 = self.rip - 4
- rip2 = self.rip
- self.fixCode(fixloc1, imm32(rip1 - rip2))
-
- self.freereg(node.begin) # Release register used in loop
- self.freereg(node.end)
- Error('No implementation of FOR statement')
-
- elif type(node) is AsmCode:
- def processOperand(op):
- if type(op) is list:
- if type(op[0]) is Variable:
- var = op[0]
- if var.isLocal:
- return ['rbp', var.offset]
- else:
- Error('Can only use local variables in inline assembler')
- return op
- for asmline in node.asmcode:
- opcode, operands = asmline
- operands = [processOperand(opx) for opx in operands]
- print('assembling', opcode, *operands)
- func,nargs = opcodes[opcode]
- code = func(*operands)
- self.addCode(code)
-
- elif isinstance(node, EmptyStatement):
- pass
-
-
- elif type(node) is StringConstant:
- self.strings.append(node)
- self.data.append(node.value) # Add string to the data section
-
- elif type(node) is Designator:
- if len(node.selectors) > 0:
- self.getreg(node)
- # Load starting address
- if node.obj.isLocal:
- self.addCode( leareg64(node.reg, ['rbp', node.obj.offset]) )
- else:
- # Global variables need to be relocated...
- self.addCode(leareg64(node.reg, ['RIP', 0]))
- self.fixCode(self.rip - 4, imm32(node.obj.offset - self.rip))
- # Loop over all designators..
- for selector in node.selectors:
- if type(selector) is Index:
- # Deref an array index
- self.genexprcode(selector.index)
- self.getreg(selector)
- self.addCode( mov(selector.reg, selector.typ.elementType.size) )
- self.addCode( imulreg64(selector.reg, selector.index.reg ) )
- self.freereg(selector.index)
- self.addCode(addreg64(node.reg, selector.reg))
- self.freereg(selector)
- elif type(selector) is Field:
- print('Field')
- Error('Field not implemented')
- else:
- Error('Unknown selector')
- else:
- Error('Can only gencode for designator with selectors')
-
- else:
- print('not generating code for {0}'.format(node))
-
- def generatecode(self, ast):
- """ code generation front end """
- self.initialize()
- self.gencode(ast)
- ast.image = self.image
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/compiler.py
--- a/applications/ide/compiler/compiler.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-import hashlib
-# Import compiler components:
-from . import lexer
-from .parser import Parser
-from .codegenerator import CodeGenerator
-from .nodes import ExportedSymbol
-from .errors import CompilerException
-
-class Compiler:
- versie = '0.9.3'
-
- def __repr__(self):
- return 'LCFOS compiler {0}'.format(self.versie)
-
- def generateSignature(self, src):
- return hashlib.md5(bytes(src,encoding='ascii')).hexdigest()
-
- def compilesource(self, src):
- """ Front end that handles the stages: """
- self.errorlist = []
- # Pass 1: parsing and type checking
- tokens = lexer.tokenize(src) # Lexical stage
- p = Parser(tokens)
- try:
- ast = p.parseModule() # Parse a module
- except CompilerException as e:
- p.errorlist.append( (e.row, e.col, e.msg) )
- if len(p.errorlist) > 0:
- self.errorlist = p.errorlist
- return
- # Pass 2: code generation
- CodeGenerator().generatecode(ast)
- # Attach a signature:
- ast.signature = self.generateSignature(src)
- # Generate exported symbols:
- ast.exports = []
- for proc in ast.procs:
- if proc.public:
- sym = ExportedSymbol(proc.name, proc.typ)
- sym.imageoffset = proc.entrypoint
- ast.exports.append(sym)
- return ast
-
- def compileProject(self, project):
- mods = []
- for fname in project.files:
- print('Compiling {0}...'.format(fname))
- source = project.loadProjectFile(fname)
- mod = self.compilesource(source)
- mods.append(mod)
- return mods
-
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/display.py
--- a/applications/ide/compiler/display.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-from .nodes import *
-
-def printNode(node, indent=0):
- """
- Print visitor
- all printing goes in here
- """
- print(' '*indent+str(node))
- if type(node) is Procedure:
- print(' '*indent+' PARAMETERS:')
- for p in node.parameters:
- printNode(p, indent+4)
- if node.block:
- print(' '*indent+' CODE:')
- printNode(node.block, indent+4)
- elif type(node) is Module:
- print(node.symtable)
- printNode(node.initcode, indent+2)
- else:
- for c in node.getChildren():
- printNode(c, indent+2)
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/errors.py
--- a/applications/ide/compiler/errors.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-""" Error handling routines """
-
-class CompilerException(Exception):
- def __init__(self, msg, row=0, col=0, filename=None):
- self.msg = msg
- self.row = row
- self.col = col
- self.filename = filename
- def __repr__(self):
- return self.msg
- def __str__(self):
- return self.msg
-
-class ErrorNode:
- def __init__(self, row, col, msg):
- self.row, self.col = row,col
- self.msg = msg
-
-def Error(msg, node=None):
- if node is None:
- raise CompilerException(msg)
- else:
- raise CompilerException(msg, node.row, node.col)
-
-def printError(source, e):
- def printLine(row, txt):
- print(str(row)+':'+txt)
- if e.row == 0:
- print('Error: {0}'.format(e.msg))
- else:
- lines = source.split('\n')
- prerow = e.row - 3
- if prerow < 1:
- prerow = 1
- afterrow = e.row + 3
- if afterrow > len(lines):
- afterrow = len(lines)
-
- # print preceding source lines:
- for r in range(prerow, e.row):
- printLine(r, lines[r-1])
- # print source line containing error:
- printLine(e.row, lines[e.row-1])
- print(' '*(len(str(e.row)+':')+e.col-1) + '^ Error: {0}'.format(e.msg))
- # print trailing source line:
- for r in range(e.row+1, afterrow+1):
- printLine(r, lines[r-1])
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/lexer.py
--- a/applications/ide/compiler/lexer.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-import collections
-import re
-from .errors import CompilerException
-
-"""
- Lexical analyzer part. Splits the input character stream into tokens.
-"""
-
-# Token is used in the lexical analyzer:
-Token = collections.namedtuple('Token', 'typ val row col')
-
-keywords = ['and', 'array', 'begin', 'by', 'case', 'const', 'div', 'do', \
- 'else', 'elsif', 'end', 'false', 'for', 'if', 'import', 'in', 'is', \
- 'mod', 'module', 'nil', 'not', 'of', 'or', 'pointer', 'procedure', \
- 'record', 'repeat', 'return', 'then', 'to', 'true', 'type', 'until', 'var', \
- 'while', 'asm' ]
-
-def tokenize(s):
- """
- Tokenizer, generates an iterator that
- returns tokens!
-
- This GREAT example was taken from python re doc page!
- """
- 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'{.*}'),
- ('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)
- 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 != 'SKIP':
- 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]
- yield Token(typ, val, line, mo.start()-line_start)
- pos = mo.end()
- mo = gettok(s, pos)
- if pos != len(s):
- col = pos - line_start
- raise CompilerException('Unexpected character {0}'.format(s[pos]), line, col)
- yield Token('END', '', line, 0)
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/modules.py
--- a/applications/ide/compiler/modules.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-import struct
-from .errors import Error
-from .nodes import *
-from .builtin import integer, real, char, boolean, void
-import os.path
-
-"""
- File format for compiled modules.
- * [11] magic identifier
- * [STR] mod name
- * [STR] signature, a md5 signature of the module.
- * [I32] size of code
- * code image
- * [I32] entrypoint for initcode
- * imported modules
- ** [I32] num of imported modules
- *** [STR] name of module
- *** signature of the module
- *** [I32] offset in the process image where the interface symbols must be placed
- * public interface
- ** [I32] num of interface elements
- *** [STR] proc name
- *** [I32] offset in code image
- *** [type] return type
- *** [I32] number of parameters
- **** parameter
- ***** parameter kind
- ***** parameter name
- ***** parameter type
-"""
-
-MAGIC = b'LCFOSMODC'
-
-loadedModules = []
-
-def loadModule(modname):
- """ returns a Module object specified by a name """
- # Check if the module was already loaded:
- for mod in loadedModules:
- if mod.name == modname:
- return mod
-
- # Try to load the module from file:
- srcfilename = modname + '.mod'
- binfilename = modname + '.bin'
- sourceExists = os.path.exists(srcfilename)
- if os.path.exists(binfilename):
- if sourceExists:
- compileModule()
- else:
- return loadModuleFromFile(binfilename)
- else:
- Error("Cannot load module '{0}'!".format(modname))
-
-def loadModuleFromFile(filename):
- f = open(filename, 'rb')
- magic = f.read(len(MAGIC))
- assert(magic == MAGIC)
-
- # Helper functions:
- def readI32():
- int32, = struct.unpack(' 0:
- operands.append( parseOperand() )
- n = n - 1
- while n > 0:
- self.Consume(',')
- operands.append(parseOperand())
- n = n - 1
- return operands
- self.Consume('asm')
- asmcode = []
- while self.token.typ != 'end':
- opcode = parseOpcode()
- func, numargs = assembler.opcodes[opcode]
- operands = parseOperands(numargs)
- asmcode.append( (opcode, operands) )
- #print('opcode', opcode, operands)
- self.Consume('end')
- return AsmCode(asmcode)
-
- def parseStatement(self):
- try:
- # Determine statement type based on the pending token:
- if self.token.typ == 'if':
- return self.parseIfStatement()
- elif self.token.typ == 'case':
- return self.parseCaseStatement()
- elif self.token.typ == 'while':
- return self.parseWhileStatement()
- elif self.token.typ == 'repeat':
- return self.parseRepeatStatement()
- elif self.token.typ == 'for':
- return self.parseForStatement()
- elif self.token.typ == 'asm':
- return self.parseAsmcode()
- elif self.token.typ == 'ID':
- # Assignment or procedure call
- designator = self.parseDesignator()
- if self.token.typ == '(' and type(designator.typ) is ProcedureType:
- return self.parseProcedureCall(designator)
- elif self.token.typ == ':=':
- return self.parseAssignment(designator)
- else:
- self.Error('Unknown statement following designator: {0}'.format(self.token))
- else:
- # TODO: return empty statement??:
- return EmptyStatement()
- self.Error('Unknown statement {0}'.format(self.token))
- except CompilerException as e:
- print(e)
- self.errorlist.append( (e.row, e.col, e.msg))
- # Do error recovery by skipping all tokens until next ; or end
- while not (self.token.typ == ';' or self.token.typ == 'end'):
- self.Consume(self.token.typ)
- return EmptyStatement()
-
- def parseStatementSequence(self):
- """ Sequence of statements seperated by ';' """
- statements = [ self.parseStatement() ]
- while self.hasConsumed(';'):
- statements.append( self.parseStatement() )
- return StatementSequence( statements )
-
- # Parsing expressions:
- """
- grammar of expressions:
- expression = SimpleExpression [ reloperator SimpleExpression ]
- reloperator = '=' | '<=' | '>=' | '<>'
- Simpleexpression = [ '+' | '-' ] term { addoperator term }
- addoperator = '+' | '-' | 'or'
- term = factor { muloperator factor }
- muloperator = '*' | '/' | 'div' | 'mod' | 'and'
- factor = number | nil | true | false | "(" expression ")" |
- designator [ actualparameters ] | 'not' factor
- """
- def parseExpression(self):
- """ The connector between the boolean and expression domain """
- expr = self.parseSimpleExpression()
- if self.token.typ in ['>=','<=','<','>','<>','=']:
- relop = self.Consume()
- expr2 = self.parseSimpleExpression()
- # Automatic type convert to reals:
- if isType(expr.typ, real) and isType(expr2.typ, integer):
- expr2 = Unop(expr2, 'INTTOREAL', real)
- if isType(expr2.typ, real) and isType(expr.typ, integer):
- expr = Unop(expr, 'INTTOREAL', real)
- # Type check:
- if not isType(expr.typ, expr2.typ):
- self.Error('Type mismatch in relop')
- if isType(expr.typ, real) and relop in ['<>', '=']:
- self.Error('Cannot check real values for equality')
-
- expr = Relop(expr, relop, expr2, boolean)
- return expr
-
- # Parsing arithmatic expressions:
- def parseTerm(self):
- a = self.parseFactor()
- while self.token.typ in ['*', '/', 'mod', 'div', 'and']:
- loc = self.getLocation()
- op = self.Consume()
- b = self.parseTerm()
- # Type determination and checking:
- if op in ['mod', 'div']:
- if not isType(a.typ, integer):
- self.Error('First operand should be integer, not {0}'.format(a.typ))
- if not isType(b.typ, integer):
- self.Error('Second operand should be integer, not {0}'.format(b.typ))
- typ = integer
- elif op == '*':
- if isType(a.typ, integer) and isType(b.typ, integer):
- typ = integer
- elif isType(a.typ, real) or isType(b.typ, real):
- if isType(a.typ, integer):
- # Automatic type cast
- a = Unop(a, 'INTTOREAL', real)
- if isType(b.typ, integer):
- b = Unop(b, 'INTTOREAL', real)
- if not isType(a.typ, real):
- self.Error('first operand must be a real!')
- if not isType(b.typ, real):
- self.Error('second operand must be a real!')
- typ = real
- else:
- self.Error('Unknown operands for multiply: {0}, {1}'.format(a, b))
- elif op == '/':
- # Division always yields a real result, for integer division use div
- if isType(a.typ, integer):
- # Automatic type cast
- a = Unop(a, 'INTTOREAL', real)
- if isType(b.typ, integer):
- b = Unop(b, 'INTTOREAL', real)
- if not isType(a.typ, real):
- self.Error('first operand must be a real!')
- if not isType(b.typ, real):
- self.Error('second operand must be a real!')
- typ = real
- elif op == 'and':
- if not isType(a.typ, boolean):
- self.Error('First operand of and must be boolean')
- if not isType(b.typ, boolean):
- self.Error('Second operand of and must be boolean')
- typ = boolean
- else:
- self.Error('Unknown operand {0}'.format(op))
-
- a = self.setLocation(Binop(a, op, b, typ), loc)
- return a
-
- def parseFactor(self):
- if self.hasConsumed('('):
- e = self.parseExpression()
- self.Consume(')')
- return e
- elif self.token.typ == 'NUMBER':
- loc = self.getLocation()
- val = self.Consume('NUMBER')
- return self.setLocation(Constant(val, integer), loc)
- elif self.token.typ == 'REAL':
- loc = self.getLocation()
- val = self.Consume('REAL')
- return self.setLocation(Constant(val, real), loc)
- elif self.token.typ == 'CHAR':
- val = self.Consume('CHAR')
- return Constant(val, char)
- elif self.token.typ == 'STRING':
- txt = self.Consume('STRING')
- return StringConstant(txt)
- elif self.token.typ in ['true', 'false']:
- val = self.Consume()
- val = True if val == 'true' else False
- return Constant(val, boolean)
- elif self.hasConsumed('nil'):
- return Constant(0, NilType())
- elif self.hasConsumed('not'):
- f = self.parseFactor()
- if not isType(f.typ, boolean):
- self.Error('argument of boolean negation must be boolean type')
- return Unop(f, 'not', boolean)
- elif self.token.typ == 'ID':
- designator = self.parseDesignator()
- # TODO: handle functions different here?
- if self.token.typ == '(' and type(designator.typ) is ProcedureType:
- return self.parseProcedureCall(designator)
- else:
- return designator
- else:
- self.Error('Expected NUMBER, ID or ( expr ), got'+str(self.token))
-
- def parseSimpleExpression(self):
- """ Arithmatic expression """
- if self.token.typ in ['+', '-']:
- # Handle the unary minus
- op = self.Consume()
- a = self.parseTerm()
- typ = a.typ
- if not isType(typ,real) and not isType(typ, integer):
- self.Error('Unary minus or plus can be only applied to real or integers')
- if op == '-':
- a = Unop(a, op, typ)
- else:
- a = self.parseTerm()
- while self.token.typ in ['+', '-', 'or']:
- loc = self.getLocation()
- op = self.Consume()
- b = self.parseTerm()
- if op in ['+', '-']:
- if isType(a.typ, real) or isType(b.typ, real):
- typ = real
- if isType(a.typ, integer):
- # Automatic type cast
- a = Unop(a, 'INTTOREAL', real)
- if not isType(a.typ, real):
- self.Error('first operand must be a real!')
- if isType(b.typ, integer):
- b = Unop(b, 'INTTOREAL', real)
- if not isType(b.typ, real):
- self.Error('second operand must be a real!')
- elif isType(a.typ, integer) and isType(b.typ, integer):
- typ = integer
- else:
- self.Error('Invalid types {0} and {1}'.format(a.typ, b.typ))
- elif op == 'or':
- if not isType(a.typ, boolean):
- self.Error('first operand must be boolean for or operation')
- if not isType(b.typ, boolean):
- self.Error('second operand must be boolean for or operation')
- typ = boolean
- else:
- self.Error('Unknown operand {0}'.format(op))
- a = self.setLocation(Binop(a, op, b, typ), loc)
- return a
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/compiler/symboltable.py
--- a/applications/ide/compiler/symboltable.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-from .nodes import *
-from .errors import Error
-
-class SymbolTable:
- """
- Symbol table for a current scope.
- It has functions:
- - hasname for checking for a name in current scope or above
- - addSymbol to add an object
- """
- def __init__(self, parent=None):
- self.parent = parent
- self.syms = {}
-
- def __repr__(self):
- return 'Symboltable with {0} symbols\n'.format(len(self.syms))
-
- def printTable(self, indent=0):
- for name in self.syms:
- print(self.syms[name])
-
- def getAllLocal(self, cls):
- """ Get all local objects of a specific type """
- r = []
- for key in self.syms.keys():
- sym = self.syms[key]
- if issubclass(type(sym), cls):
- r.append(sym)
- return r
-
- def getLocal(self, cls, name):
- if name in self.syms.keys():
- sym = self.syms[name]
- if isinstance(sym, cls):
- return sym
- else:
- Error('Wrong type found')
- else:
- Error('Symbol not found')
-
- # Retrieving of specific classes of items:
- def get(self, cls, name):
- if self.hasSymbol(name):
- sym = self.getSymbol(name)
- if issubclass(type(sym), cls):
- return sym
- raise SymbolException('type {0} undefined'.format(typename))
-
- def has(self, cls, name):
- if self.hasSymbol(name):
- sym = self.getSymbol(name)
- if issubclass(type(sym), cls):
- return True
- return False
-
- # Adding and retrieving of symbols in general:
- def addSymbol(self, sym):
- if sym.name in self.syms.keys():
- raise Exception('Symbol "{0}" redefined'.format(sym.name))
- else:
- self.syms[sym.name] = sym
-
- def getSymbol(self, name):
- if name in self.syms.keys():
- return self.syms[name]
- else:
- if self.parent:
- return self.parent.getSymbol(name)
- else:
- Error('Symbol "{0}" undeclared!'.format(name))
-
- def hasSymbol(self, name):
- if name in self.syms.keys():
- return True
- else:
- if self.parent:
- return self.parent.hasSymbol(name)
- else:
- return False
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/data/lcfos.png
Binary file applications/ide/data/lcfos.png has changed
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/ide/__init__.py
--- a/applications/ide/ide/__init__.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-# Package
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/ide/astviewer.py
--- a/applications/ide/ide/astviewer.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-def astToNamedElement(astNode, parentNode):
- """ Helper to convert and AST tree to NamedElement tree: """
- item = QStandardItem(str(astNode))
- item.setData(astNode)
- parentNode.appendRow(item)
- for c in astNode.getChildren():
- astToNamedElement(c, item)
-
-# The actual widget:
-class AstViewer(QTreeView):
- sigNodeSelected = pyqtSignal(object)
- def __init__(self, parent=None):
- super(AstViewer, self).__init__(parent)
- self.setHeaderHidden(True)
- self.clicked.connect(self.selectHandler)
-
- def setAst(self, ast):
- """ Create a new model and add all ast elements to it """
- model = QStandardItemModel()
- if ast:
- astToNamedElement(ast, model.invisibleRootItem())
- self.setModel( model )
- self.expandAll()
-
- def selectHandler(self, index):
- if not index.isValid():
- return
- model = self.model()
- item = model.itemFromIndex(index)
- node = item.data()
- self.sigNodeSelected.emit(node)
-
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/ide/codeeditor.py
--- a/applications/ide/ide/codeeditor.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-import compiler.lexer
-import os.path
-
-class MySyntaxHighlighter(QSyntaxHighlighter):
- def __init__(self, parent=None):
- super(MySyntaxHighlighter, self).__init__(parent)
- # Syntax highlighting:
- self.rules = []
- fmt = QTextCharFormat()
- fmt.setForeground(Qt.darkBlue)
- fmt.setFontWeight(QFont.Bold)
- for kw in compiler.lexer.keywords:
- pattern = '\\b'+kw+'\\b'
- self.rules.append( (pattern, fmt) )
-
- # Comments:
- fmt = QTextCharFormat()
- fmt.setForeground(Qt.gray)
- fmt.setFontItalic(True)
- pattern = '\{.*\}'
- self.rules.append( (pattern, fmt) )
-
- # Procedure:
- fmt = QTextCharFormat()
- fmt.setForeground(Qt.blue)
- fmt.setFontItalic(True)
- #pattern = '(?<=procedure )[A-Za-z]'
- # TODO lookbehind does not work, think something else
- #self.rules.append( (pattern, fmt) )
-
- def highlightBlock(self, text):
- for pattern, fmt in self.rules:
- expression = QRegExp(pattern)
- index = expression.indexIn(text)
- while index >= 0:
- length = expression.matchedLength()
- self.setFormat(index, length, fmt)
- index = expression.indexIn(text, index + length)
-
-class LineNumberArea(QWidget):
- def __init__(self, codeedit):
- super(LineNumberArea, self).__init__(codeedit)
- self.codeedit = codeedit
- # TODO: display error in this: self.setToolTip('hello world')
- def sizeHint(self):
- return QSize(self.codeedit.lineNumberAreaWidth(), 0)
- def paintEvent(self, ev):
- self.codeedit.lineNumberAreaPaintEvent(ev)
-
-class CodeEdit(QPlainTextEdit):
- def __init__(self, parent=None):
- super(CodeEdit, self).__init__(parent)
- # members:
- self.isUntitled = True
- self.filename = None
- self.setFont(QFont('Courier'))
- self.lineNumberArea = LineNumberArea(self)
-
- self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
- self.updateRequest.connect(self.updateLineNumberArea)
-
- # Syntax highlighter:
- self.highlighter = MySyntaxHighlighter(self.document())
-
- def setFileName(self, filename):
- self.filename = filename
- self.isUntitled = False
- self.setWindowTitle(filename)
- def setSource(self, source):
- self.setPlainText(source)
-
- def save(self):
- pass
- def saveAs(self):
- pass
-
- def saveFile(self):
- if self.isUntitled:
- self.saveAs()
- else:
- source = str(self.toPlainText())
- f = open(self.filename, 'w')
- f.write(source)
- f.close()
-
- def highlightErrorLocation(self, row, col):
- tc = QTextCursor(self.document())
- tc.clearSelection()
- tc.movePosition(tc.Down, tc.MoveAnchor, row - 1)
- tc.movePosition(tc.Right, tc.MoveAnchor, col - 1)
- tc.movePosition(tc.NextCharacter, tc.KeepAnchor) # Select 1 character
- selection = QTextEdit.ExtraSelection()
- lineColor = QColor(Qt.red).lighter(160)
- selection.format.setBackground(lineColor)
- #selection.format.setProperty(QTextFormat.FullWidthSelection, True)
- selection.cursor = tc
- self.setExtraSelections( [ selection ] )
- def clearErrors(self):
- self.setExtraSelections( [ ] )
-
- def lineNumberAreaWidth(self):
- digits = 1
- mx = max(1, self.blockCount())
- while mx >= 10:
- mx = mx / 10
- digits += 1
- space = 3 + self.fontMetrics().width('8') * digits
- return space
- def lineNumberAreaPaintEvent(self, ev):
- painter = QPainter(self.lineNumberArea)
- painter.fillRect(ev.rect(), Qt.lightGray)
- block = self.firstVisibleBlock()
- blockNumber = block.blockNumber()
- top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
- bottom = top + self.blockBoundingRect(block).height()
- while block.isValid() and top <= ev.rect().bottom():
- if block.isVisible() and bottom >= ev.rect().top():
- num = str(blockNumber + 1)
- painter.setPen(Qt.black)
- painter.drawText(0, top, self.lineNumberArea.width(), self.fontMetrics().height(), Qt.AlignRight, num)
- block = block.next()
- top = bottom
- bottom = top + self.blockBoundingRect(block).height()
- blockNumber += 1
- def resizeEvent(self, ev):
- super(CodeEdit, self).resizeEvent(ev)
- cr = self.contentsRect()
- self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height() ))
- def updateLineNumberAreaWidth(self, newBlockCount):
- self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
- def updateLineNumberArea(self, rect, dy):
- if dy > 0:
- self.lineNumberArea.scroll(0, dy)
- else:
- self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height())
- if rect.contains(self.viewport().rect()):
- self.updateLineNumberAreaWidth(0)
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/ide/ide.py
--- a/applications/ide/ide/ide.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,309 +0,0 @@
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-# ide components:
-from .codeeditor import CodeEdit
-from .astviewer import AstViewer
-import base64
-from project import Project
-import os.path
-
-lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n')
-
-class BuildOutput(QTextEdit):
- """ Build output component """
- def __init__(self, parent=None):
- super(BuildOutput, self).__init__(parent)
- self.setCurrentFont(QFont('Courier'))
- self.setReadOnly(True)
- self.append('Build output will appear here!')
-
-class BuildErrors(QListView):
- sigErrorSelected = pyqtSignal(object)
- def __init__(self, parent=None):
- super(BuildErrors, self).__init__(parent)
- model = QStandardItemModel()
- self.setModel(model)
- self.clicked.connect(self.itemSelected)
- def setErrorList(self, errorlist):
- model = QStandardItemModel()
- for e in errorlist:
- row, col, msg = e
- item = QStandardItem(str(msg))
- item.setData(e)
- model.appendRow(item)
- self.setModel(model)
- def itemSelected(self, index):
- if not index.isValid():
- return
- model = self.model()
- item = model.itemFromIndex(index)
- err = item.data()
- self.sigErrorSelected.emit(err)
-
-class ProjectView(QWidget):
- sigLoadFile = pyqtSignal(str)
- def __init__(self, parent=None):
- super(ProjectView, self).__init__(parent)
- self.treeview = QTreeView(self)
- self.treeview.setContextMenuPolicy(Qt.CustomContextMenu)
- l = QVBoxLayout(self)
- l.addWidget(self.treeview)
- pm = QPixmap()
- pm.loadFromData(lcfospng)
- self.projectIcon = QIcon(pm)
- # Connect signals:
- self.treeview.activated.connect(self.activate)
- self.treeview.customContextMenuRequested.connect(self.contextMenu)
- def setProject(self, project):
- self.project = project
- model = QStandardItemModel()
- root = model.invisibleRootItem()
- pitem = QStandardItem(self.projectIcon, project.name)
- pitem.setEditable(False)
- pitem.setData(project)
- root.appendRow(pitem)
- for f in self.project.files:
- fitem = QStandardItem(f)
- pitem.appendRow(fitem)
- fitem.setEditable(False)
- fitem.setData(f)
- self.treeview.setModel(model)
- self.treeview.expandAll()
- def contextMenu(self, pos):
- idx = self.treeview.indexAt(pos)
- if not idx.isValid():
- return
- item = self.treeview.model().itemFromIndex(idx)
- def activate(self, index):
- if not index.isValid():
- return
- model = self.treeview.model()
- item = model.itemFromIndex(index)
- fn = item.data()
- if type(fn) is str:
- self.sigLoadFile.emit(fn)
-
-class AboutDialog(QDialog):
- def __init__(self, parent=None):
- super(AboutDialog, self).__init__(parent)
- self.setWindowTitle('About')
- l = QVBoxLayout(self)
- txt = QTextEdit(self)
- txt.setReadOnly(True)
- aboutText = """lcfOS IDE
-
An all-in-one IDE for OS development.
- https://www.assembla.com/spaces/lcfOS/wiki
- Author: Windel Bouwman
- """
- txt.append(aboutText)
- l.addWidget(txt)
- but = QPushButton('OK')
- but.setDefault(True)
- but.clicked.connect(self.close)
- l.addWidget(but)
-
-class ProjectOptions(QDialog):
- pass
- # TODO: project options in here
-
-class Ide(QMainWindow):
- def __init__(self, parent=None):
- super(Ide, self).__init__(parent)
- self.setWindowTitle('LCFOS IDE')
- icon = QPixmap()
- icon.loadFromData(lcfospng)
- self.setWindowIcon(QIcon(icon))
-
- # Create menus:
- self.fileMenu = self.menuBar().addMenu('File')
- self.viewMenu = self.menuBar().addMenu('View')
- self.projectMenu = self.menuBar().addMenu('Project')
- self.helpMenu = self.menuBar().addMenu('Help')
-
- # Create mdi area:
- self.mdiArea = QMdiArea()
- self.setCentralWidget(self.mdiArea)
-
- # Create components:
- self.buildOutput = BuildOutput()
- self.addComponent('Build output', self.buildOutput)
-
- self.astViewer = AstViewer()
- self.addComponent('AST viewer', self.astViewer)
- self.astViewer.sigNodeSelected.connect(self.nodeSelected)
-
- self.builderrors = BuildErrors()
- self.addComponent('Build errors', self.builderrors)
- self.builderrors.sigErrorSelected.connect(self.errorSelected)
-
- self.projectview = ProjectView()
- self.addComponent('Project', self.projectview)
- self.projectview.sigLoadFile.connect(self.loadFile)
-
- # About dialog:
- self.aboutDialog = AboutDialog()
- self.aboutDialog.setWindowIcon(QIcon(icon))
- # Create actions:
- self.buildAction = QAction('Build!', self)
- self.buildAction.setShortcut(QKeySequence('F7'))
- self.projectMenu.addAction(self.buildAction)
- self.buildAction.triggered.connect(self.buildFile)
- self.openProjectAction = QAction("Open project", self)
- self.openProjectAction.triggered.connect(self.openProject)
- self.projectMenu.addAction(self.openProjectAction)
- self.helpAction = QAction('Help', self)
- self.helpAction.setShortcut(QKeySequence('F1'))
- self.helpMenu.addAction(self.helpAction)
- self.aboutAction = QAction('About', self)
- self.helpMenu.addAction(self.aboutAction)
- self.aboutAction.triggered.connect(self.aboutDialog.open)
-
- self.newFileAction = QAction("New File", self)
- self.fileMenu.addAction(self.newFileAction)
- self.newFileAction.triggered.connect(self.newFile)
- self.saveFileAction = QAction("Save File", self)
- self.fileMenu.addAction(self.saveFileAction)
- self.saveFileAction.triggered.connect(self.saveFile)
- self.closeFileAction = QAction("Close File", self)
- self.fileMenu.addAction(self.closeFileAction)
- self.closeFileAction.triggered.connect(self.closeFile)
-
- cascadeAction = QAction("Cascade windows", self)
- cascadeAction.triggered.connect(self.mdiArea.cascadeSubWindows)
- self.viewMenu.addAction(cascadeAction)
- tileAction = QAction('Tile windows', self)
- tileAction.triggered.connect(self.mdiArea.tileSubWindows)
- self.viewMenu.addAction(tileAction)
-
- # Load settings:
- self.settings = QSettings('windelsoft', 'lcfoside')
- self.loadSettings()
-
- def addComponent(self, name, widget):
- dw = QDockWidget(name)
- dw.setWidget(widget)
- dw.setObjectName(name)
- self.addDockWidget(Qt.RightDockWidgetArea, dw)
- self.viewMenu.addAction(dw.toggleViewAction())
-
- # File handling:
- def newFile(self):
- ce = CodeEdit()
- w = self.mdiArea.addSubWindow(ce)
- ce.show()
-
- def saveFile(self):
- ac = self.activeMdiChild()
- if ac:
- ac.saveFile()
-
- def saveAll(self):
- pass
-
- def openFile(self):
- # TODO
- pass
-
- def closeFile(self):
- ac = self.activeMdiChild()
- if ac:
- self.mdiArea.removeSubWindow(ac)
-
- def loadFile(self, filename):
- # Find existing mdi widget:
- wid = self.findMdiChild(filename)
- if wid:
- self.mdiArea.setActiveSubWindow(wid.parent())
- return wid
-
- # Create a new one:
- ce = CodeEdit()
- source = self.project.loadProjectFile(filename)
- ce.setSource(source)
- self.mdiArea.addSubWindow(ce)
- ce.show()
- return ce
-
- # MDI:
- def activeMdiChild(self):
- aw = self.mdiArea.activeSubWindow()
- if aw:
- return aw.widget()
- else:
- return None
-
- def findMdiChild(self, filename):
- for window in self.mdiArea.subWindowList():
- wid = window.widget()
- if wid.filename == filename:
- return wid
- return None
-
- def allChildren(self):
- c = []
- for window in self.mdiArea.subWindowList():
- wid = window.widget()
- c.append(wid)
- return c
-
- # Settings:
- def loadSettings(self):
- if self.settings.contains('mainwindowstate'):
- self.restoreState(self.settings.value('mainwindowstate'))
- if self.settings.contains('mainwindowgeometry'):
- self.restoreGeometry(self.settings.value('mainwindowgeometry'))
- if self.settings.contains('openedproject'):
- projectfile = self.settings.value('openedproject')
- self.loadProject(projectfile)
-
- def closeEvent(self, ev):
- self.settings.setValue('mainwindowstate', self.saveState())
- self.settings.setValue('mainwindowgeometry', self.saveGeometry())
- if self.project:
- self.settings.setValue('openedproject', self.project.filename)
- # TODO: ask for save of opened files
- ev.accept()
-
- # Error handling:
- def nodeSelected(self, node):
- ce = self.activeMdiChild()
- if not ce:
- return
- if node.location:
- row, col = node.location
- ce.highlightErrorLocation( row, col )
- else:
- ce.clearErrors()
-
- def errorSelected(self, err):
- row, col, msg = err
- ce = self.activeMdiChild()
- if not ce:
- return
- ce.highlightErrorLocation(row, col)
-
- # Project loading:
- def loadProject(self, filename):
- self.project = Project(filename)
- self.projectview.setProject(self.project)
-
- def openProject(self):
- filename = QFileDialog.getOpenFileName(self, \
- "Choose project file", "", "lcfos Project files (*.lcp)")
- if filename:
- self.loadProject(filename)
-
- # Build recepy:
- def buildFile(self):
- """ Build project """
- self.saveAll()
- self.buildOutput.clear()
- self.buildOutput.append(str(self.compiler))
- mods = self.compiler.compileProject(self.project)
-
- self.builderrors.setErrorList(self.compiler.errorlist)
- self.astViewer.setAst(mods[0])
- for err in self.compiler.errorlist:
- self.buildOutput.append(str(err))
- self.buildOutput.append("Done!")
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/project.py
--- a/applications/ide/project.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-"""
- Project that can be stored to and loaded from XML.
-"""
-
-from xml.sax import ContentHandler, make_parser
-import xml.dom.minidom as md
-import os.path
-
-class ProjectContentHandler(ContentHandler):
- def __init__(self, project):
- self.project = project
- self.inFiles = False
- def startElement(self, name, attrs):
- if name == 'Project':
- self.project.name = attrs['name']
- if name == 'Files':
- self.inFiles = True
- if name == 'File' and self.inFiles:
- self.project.files.append(attrs['Filename'])
- def endElement(self, name):
- if name == 'Files':
- self.inFiles = False
-
-class ProjectFile:
- def __init__(self, filename):
- self.filename = filename
-
-class Project:
- def __init__(self, filename, isnew=False):
- self.name = ""
- self.files = []
- self.settings = {}
- self.filename = filename
-
- if not isnew:
- """ Load the project from the XML file """
- parser = make_parser()
- handler = ProjectContentHandler(self)
- parser.setContentHandler(handler)
- parser.parse(filename)
-
- def save(self, filename):
- """ Save the project in XML format """
- # Create document:
- doc = md.Document()
- # Add project:
- project = doc.createElement("Project")
- project.setAttribute("name", self.name)
- doc.appendChild(project)
-
- # Add project files:
- filesNode = doc.createElement("Files")
- project.appendChild(filesNode)
- for f in self.files:
- fe = doc.createElement("File")
- fe.setAttribute("Filename", f)
- filesNode.appendChild(fe)
-
- # Write the XML file:
- xml = doc.toprettyxml()
- with open(filename, 'w') as f:
- f.write(xml)
-
- def loadProjectFile(self, filename):
- basedir = os.path.dirname(self.filename)
- filename = os.path.join(basedir, filename)
- with open(filename, 'r') as f:
- src = f.read()
- return src
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/runbuild.py
--- a/applications/ide/runbuild.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-import sys
-
-# Compiler imports:
-from compiler.compiler import Compiler
-from project import Project
-
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print('Use {0} projectfile'.format(sys.argv[0]))
- sys.exit(-1)
- filename = sys.argv[1]
- project = Project()
- project.load(filename)
- pc = Compiler()
- pc.compileProject(project)
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/runide.py
--- a/applications/ide/runide.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-import sys
-from PyQt4.QtGui import QApplication
-
-# Compiler imports:
-from compiler.compiler import Compiler
-from ide.ide import Ide
-
-if __name__ == '__main__':
- app = QApplication(sys.argv)
- ide = Ide()
- ide.compiler = Compiler()
- ide.show()
- app.exec_()
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/runtests.py
--- a/applications/ide/runtests.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-import unittest
-import os
-
-from compiler.compiler import Compiler
-from compiler.errors import CompilerException, printError
-from compiler import lexer
-from compiler.parser import Parser
-from compiler import assembler
-from compiler.codegenerator import CodeGenerator
-from project import Project
-
-class CompilerTestCase(unittest.TestCase):
- """ test methods start with 'test*' """
- def testSource1(self):
- source = """
- module lcfos;
- var
- a : integer;
-
- procedure putchar(num : integer);
- begin
- end putchar;
-
- procedure WriteNum( num: integer);
- var
- d, base : integer;
- dgt : integer;
- begin
- d := 1;
- base := 10;
- while num div d >= base do
- d := d * base
- end;
- while d <> 0 do
- dgt := num div d;
- num := num mod d;
- d := d div base;
- putchar(48 + dgt)
- end
- end WriteNum;
-
- begin
- a := 1;
- while a < 26
- do
- putchar(65+a);
- a := a * 2
- end;
- end lcfos.
- """
- pc = Compiler()
- pc.compilesource(source)
- def testSource2(self):
- source = """
- module lcfos;
- var
- a, b : integer;
- arr: array 30 of integer;
- arr2: array 10, 12 of integer;
- procedure t2*() : integer;
- begin
- a := 2;
- while a < 5 do
- b := arr[a-1] + arr[a-2];
- arr2[a,2] := b;
- arr2[a,3] := arr2[a,2] + arr2[a,2]*3 + b;
- arr[a] := b;
- a := a + 1;
- end;
- return b
- end t2;
- begin
- b := 12;
- arr[0] := 1;
- arr[1] := 1;
- end lcfos.
- """
- pc = Compiler()
- mod = pc.compilesource(source)
- def testSource5(self):
- source = """
- module lcfos;
- procedure WriteLn() : integer;
- const zzz = 13;
- var
- a, b, c: integer;
- begin
- a := 2;
- b := 7;
- c := 10 * a + b*10*a;
- return c
- end WriteLn;
- begin end lcfos.
- """
- pc = Compiler()
- pc.compilesource(source)
- def tstForStatement(self):
- source = """
- module fortest;
- var
- a,b,c : integer;
- begin
- c := 0;
- for a := 1 to 10 by 1 do
- b := a + 15;
- c := c + b * a;
- end;
- end fortest.
- """
- pc = Compiler()
- pc.compilesource(source)
- def testSourceIfAndWhilePattern(self):
- source = """
- module lcfos;
- procedure WriteLn() : integer;
- const zzz = 13;
- var
- a, b, c: integer;
- begin
- a := 1;
- b := 2;
- if a * 3 > b then
- c := 10*a + b*10*a*a*a*b;
- else
- c := 13;
- end;
- while a < 101 do
- a := a + 1;
- c := c + 2;
- end;
- return c
- end WriteLn;
- begin end lcfos.
- """
- pc = Compiler()
- pc.compilesource(source)
-
- def testPattern1(self):
- """ Test if expression can be compiled into byte code """
- src = "12*13+33-12*2*3"
- tokens = lexer.tokenize(src)
- ast = Parser(tokens).parseExpression()
- code = CodeGenerator().genexprcode(ast)
-
- def testAssembler(self):
- """ Check all kind of assembler cases """
- assert(assembler.shortjump(5) == [0xeb, 0x5])
- assert(assembler.shortjump(-2) == [0xeb, 0xfc])
- assert(assembler.shortjump(10,'GE') == [0x7d, 0xa])
- assert(assembler.nearjump(5) == [0xe9, 0x5,0x0,0x0,0x0])
- assert(assembler.nearjump(-2) == [0xe9, 0xf9, 0xff,0xff,0xff])
- assert(assembler.nearjump(10,'LE') == [0x0f, 0x8e, 0xa,0x0,0x0,0x0])
-
- def testCall(self):
- assert(assembler.call('r10') == [0x41, 0xff, 0xd2])
- assert(assembler.call('rcx') == [0xff, 0xd1])
- def testXOR(self):
- assert(assembler.xorreg64('rax', 'rax') == [0x48, 0x31, 0xc0])
- assert(assembler.xorreg64('r9', 'r8') == [0x4d, 0x31, 0xc1])
- assert(assembler.xorreg64('rbx', 'r11') == [0x4c, 0x31, 0xdb])
-
- def testINC(self):
- assert(assembler.increg64('r11') == [0x49, 0xff, 0xc3])
- assert(assembler.increg64('rcx') == [0x48, 0xff, 0xc1])
-
- def testPush(self):
- assert(assembler.push('rbp') == [0x55])
- assert(assembler.push('rbx') == [0x53])
- assert(assembler.push('r12') == [0x41, 0x54])
- def testPop(self):
- assert(assembler.pop('rbx') == [0x5b])
- assert(assembler.pop('rbp') == [0x5d])
- assert(assembler.pop('r12') == [0x41, 0x5c])
-
- def testAsmLoads(self):
- # TODO constant add testcases
- assert(assembler.mov('rbx', 'r14') == [0x4c, 0x89, 0xf3])
- assert(assembler.mov('r12', 'r8') == [0x4d, 0x89, 0xc4])
- assert(assembler.mov('rdi', 'rsp') == [0x48, 0x89, 0xe7])
-
- def testAsmMemLoads(self):
- assert(assembler.mov('rax', ['r8','r15',0x11]) == [0x4b,0x8b,0x44,0x38,0x11])
- assert(assembler.mov('r13', ['rbp','rcx',0x23]) == [0x4c,0x8b,0x6c,0xd,0x23])
-
- assert(assembler.mov('r9', ['rbp',-0x33]) == [0x4c,0x8b,0x4d,0xcd])
- #assert(assembler.movreg64('rbx', ['rax']) == [0x48, 0x8b,0x18])
-
- assert(assembler.mov('rax', [0xb000]) == [0x48,0x8b,0x4,0x25,0x0,0xb0,0x0,0x0])
- assert(assembler.mov('r11', [0xa0]) == [0x4c,0x8b,0x1c,0x25,0xa0,0x0,0x0,0x0])
-
- assert(assembler.mov('r11', ['RIP', 0xf]) == [0x4c,0x8b,0x1d,0x0f,0x0,0x0,0x0])
-
- def testAsmMemStores(self):
- assert(assembler.mov(['rbp', 0x13],'rbx') == [0x48,0x89,0x5d,0x13])
- assert(assembler.mov(['r12', 0x12],'r9') == [0x4d,0x89,0x4c,0x24,0x12])
- assert(assembler.mov(['rcx', 0x11],'r14') == [0x4c,0x89,0x71,0x11])
-
-
- assert(assembler.mov([0xab], 'rbx') == [0x48,0x89,0x1c,0x25,0xab,0x0,0x0,0x0])
- assert(assembler.mov([0xcd], 'r13') == [0x4c,0x89,0x2c,0x25,0xcd,0x0,0x0,0x0])
-
- assert(assembler.mov(['RIP', 0xf], 'r9') == [0x4c,0x89,0x0d,0x0f,0x0,0x0,0x0])
-
- def testAsmMOV8(self):
- assert(assembler.mov(['rbp', -8], 'al') == [0x88, 0x45, 0xf8])
- assert(assembler.mov(['r11', 9], 'cl') == [0x41, 0x88, 0x4b, 0x09])
-
- assert(assembler.mov(['rbx'], 'al') == [0x88, 0x03])
- assert(assembler.mov(['r11'], 'dl') == [0x41, 0x88, 0x13])
-
- def testAsmLea(self):
- assert(assembler.leareg64('r11', ['RIP', 0xf]) == [0x4c,0x8d,0x1d,0x0f,0x0,0x0,0x0])
- assert(assembler.leareg64('rsi', ['RIP', 0x7]) == [0x48,0x8d,0x35,0x07,0x0,0x0,0x0])
-
- assert(assembler.leareg64('rcx', ['rbp', -8]) == [0x48,0x8d,0x4d,0xf8])
-
- def testAssemblerCMP(self):
- assert(assembler.cmpreg64('rdi', 'r13') == [0x4c, 0x39, 0xef])
- assert(assembler.cmpreg64('rbx', 'r14') == [0x4c, 0x39, 0xf3])
- assert(assembler.cmpreg64('r12', 'r9') == [0x4d, 0x39, 0xcc])
-
- assert(assembler.cmpreg64('rdi', 1) == [0x48, 0x83, 0xff, 0x01])
- assert(assembler.cmpreg64('r11', 2) == [0x49, 0x83, 0xfb, 0x02])
- def testAssemblerADD(self):
- assert(assembler.addreg64('rbx', 'r13') == [0x4c, 0x01, 0xeb])
- assert(assembler.addreg64('rax', 'rbx') == [0x48, 0x01, 0xd8])
- assert(assembler.addreg64('r12', 'r13') == [0x4d, 0x01, 0xec])
-
- assert(assembler.addreg64('rbx', 0x13) == [0x48, 0x83, 0xc3, 0x13])
- assert(assembler.addreg64('r11', 0x1234567) == [0x49, 0x81, 0xc3, 0x67, 0x45,0x23,0x1])
- assert(assembler.addreg64('rsp', 0x33) == [0x48, 0x83, 0xc4, 0x33])
-
- def testAssemblerSUB(self):
- assert(assembler.subreg64('rdx', 'r14') == [0x4c, 0x29, 0xf2])
- assert(assembler.subreg64('r15', 'rbx') == [0x49, 0x29, 0xdf])
- assert(assembler.subreg64('r8', 'r9') == [0x4d, 0x29, 0xc8])
-
- assert(assembler.subreg64('rsp', 0x123456) == [0x48, 0x81, 0xec, 0x56,0x34,0x12,0x0])
- assert(assembler.subreg64('rsp', 0x12) == [0x48, 0x83, 0xec, 0x12])
-
- def testAssemblerIDIV(self):
- assert(assembler.idivreg64('r11') == [0x49, 0xf7, 0xfb])
- assert(assembler.idivreg64('rcx') == [0x48, 0xf7, 0xf9])
- assert(assembler.idivreg64('rsp') == [0x48, 0xf7, 0xfc])
-
- def testAssemblerIMUL(self):
- assert(assembler.imulreg64_rax('rdi') == [0x48, 0xf7, 0xef])
- assert(assembler.imulreg64_rax('r10') == [0x49, 0xf7, 0xea])
- assert(assembler.imulreg64_rax('rdx') == [0x48, 0xf7, 0xea])
-
- assert(assembler.imulreg64('r11', 'rdi') == [0x4c, 0xf, 0xaf, 0xdf])
- assert(assembler.imulreg64('r12', 'rbx') == [0x4c, 0xf, 0xaf, 0xe3])
- # nasm generates this machine code: 0x4d, 0x6b, 0xff, 0xee
- # This also works: 4D0FAFFE (another variant?? )
- assert(assembler.imulreg64('r15', 'r14') == [0x4d, 0x0f, 0xaf, 0xfe])
- def testProject(self):
- p = Project('test.xml', isnew=True)
- p.name = "Test project"
- p.files.append('main.mod')
- p.files.append('test.mod')
- p.save('test.xml')
-
- q = Project('test.xml')
-
- assert(p.name == q.name)
- assert(p.files == q.files)
- # TODO: remove test.xml test file
- os.remove('test.xml')
-
-if __name__ == '__main__':
- unittest.main()
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/testproject/main.mod
--- a/applications/ide/testproject/main.mod Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-module main;
-var
- a,b,c : integer;
-
-procedure test(x:integer);
- var y,z:integer;
-begin
- y := x * 3 + 2;
- z := x + y + a;
-end test;
-
-procedure add(a:integer; b:integer):integer;
- var
- tmp : integer;
- begin
- tmp := a + b;
- return tmp
- end add;
-
-begin
- a := 12;
- b := a * 12 + 33;
- c := a div b + a * b * 99;
-end main.
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/testproject/test.lcp
--- a/applications/ide/testproject/test.lcp Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/ide/testproject/test.mod
--- a/applications/ide/testproject/test.mod Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-module test;
-var
- a,b,c : integer;
-
-procedure test(x:integer);
- var y,z:integer;
-begin
- y := x * 3 + 2;
- z := x + y + a;
-end test;
-
-procedure add(a:integer; b:integer):integer;
- var
- tmp : integer;
- begin
- tmp := a + b;
- return tmp
- end add;
-
-begin
- a := 12;
- b := a * 12 + 33;
- c := a div b + a * b * 99;
-end test.
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/lab/bouncing_cube.py
--- a/applications/lab/bouncing_cube.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,406 +0,0 @@
-from PyQt4.QtGui import *
-from PyQt4.QtCore import *
-from PyQt4.QtOpenGL import QGLWidget
-from OpenGL.GL import *
-from OpenGL.GLU import gluPerspective
-import sys
-from random import random
-from math import pi, cos, sin, fabs, sqrt
-from numpy import mat, array, ones, zeros, eye
-from numpy.linalg import norm
-import numpy as np
-import time
-import scipy.integrate
-#import pyopencl
-
-"""
- Test script that lets a dice bounce.
- Converted from 20-sim equations into python code.
-
- 20-sim website:
- http://www.20sim.com
-
-"""
-def drawCube(w):
- glBegin(GL_QUADS) # Start Drawing The Cube
- glColor3f(0.0,1.0,0.0) # Set The Color To Blue
- glVertex3f( w, w,-w) # Top Right Of The Quad (Top)
- glVertex3f(-w, w,-w) # Top Left Of The Quad (Top)
- glVertex3f(-w, w, w) # Bottom Left Of The Quad (Top)
- glVertex3f( w, w, w) # Bottom Right Of The Quad (Top)
-
- glColor3f(1.0,0.5,0.0) # Set The Color To Orange
- glVertex3f( w,-w, w) # Top Right Of The Quad (Bottom)
- glVertex3f(-w,-w, w) # Top Left Of The Quad (Bottom)
- glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Bottom)
- glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Bottom)
-
- glColor3f(1.0,0.0,0.0) # Set The Color To Red
- glVertex3f( w, w, w) # Top Right Of The Quad (Front)
- glVertex3f(-w, w, w) # Top Left Of The Quad (Front)
- glVertex3f(-w,-w, w) # Bottom Left Of The Quad (Front)
- glVertex3f( w,-w, w) # Bottom Right Of The Quad (Front)
-
- glColor3f(1.0,1.0,0.0) # Set The Color To Yellow
- glVertex3f( w,-w,-w) # Bottom Left Of The Quad (Back)
- glVertex3f(-w,-w,-w) # Bottom Right Of The Quad (Back)
- glVertex3f(-w, w,-w) # Top Right Of The Quad (Back)
- glVertex3f( w, w,-w) # Top Left Of The Quad (Back)
-
- glColor3f(0.0,0.0,1.0) # Set The Color To Blue
- glVertex3f(-w, w, w) # Top Right Of The Quad (Left)
- glVertex3f(-w, w,-w) # Top Left Of The Quad (Left)
- glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Left)
- glVertex3f(-w,-w, w) # Bottom Right Of The Quad (Left)
-
- glColor3f(1.0,0.0,1.0) # Set The Color To Violet
- glVertex3f( w, w,-w) # Top Right Of The Quad (Right)
- glVertex3f( w, w, w) # Top Left Of The Quad (Right)
- glVertex3f( w,-w, w) # Bottom Left Of The Quad (Right)
- glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Right)
- glEnd() # Done Drawing The Quad
-
-def drawFloor(w, h):
- glBegin(GL_QUADS) # Start Drawing The Cube
-
- glColor3f(1.0,0.5,0.0) # Set The Color To Orange
- glVertex3f( w,-w,h)# Top Right Of The Quad (Bottom)
- glVertex3f(-w,-w,h)# Top Left Of The Quad (Bottom)
- glVertex3f(-w,w,h)# Bottom Left Of The Quad (Bottom)
- glVertex3f( w,w,h)# Bottom Right Of The Quad (Bottom)
- glEnd() # Done Drawing The Quad
-
-def drawAxis():
- glLineWidth(0.5)
- glBegin(GL_LINES)
- glColor3f(1.0, 0.0, 0.0)
- glVertex3f(0,0,0)
- glVertex3f(1,0,0)
- glColor3f(0.0, 1.0, 0.0)
- glVertex3f(0,0,0)
- glVertex3f(0,1,0)
- glColor3f(0.0, 0.0, 1.0)
- glVertex3f(0,0,0)
- glVertex3f(0,0,1)
- glEnd()
-
-
-def cross(A, B):
- a = A.A1
- b = B.A1
- return mat(np.cross(a, b)).T
-
-def skew(X):
- Y = mat(zeros( (3, 3) ))
- a,b,c = X.A1
- Y[0,1] = -c
- Y[0,2] = b
- Y[1,0] = c
- Y[1,2] = -a
- Y[2,0] = -b
- Y[2,1] = a
- return Y
-
-def adjoint(T):
- W = T[0:3, 0]
- V = T[3:6, 0]
- a = mat(zeros( (6,6) ) )
- a[0:3, 0:3] = skew(W)
- a[3:6, 0:3] = skew(V)
- a[3:6, 3:6] = skew(W)
- return a
-
-def Adjoint(H):
- R = H[0:3, 0:3]
- P = H[0:3, 3]
- a = mat(zeros( (6,6) ) )
- a[0:3, 0:3] = R
- a[3:6, 3:6] = R
- a[3:6, 0:3] = skew(P) * R
- return a
-
-def quatToR(q):
- x, y, z, w = q.A1
- r = mat(eye(3))
- r[0,0] = 1 - (2*y**2+2*z**2)
- r[0,1] = 2*x*y+2*z*w
- r[0,2] = 2*x*z - 2*y*w
- r[1,0] = 2*x*y-2*z*w
- r[1,1] = 1 - (2*x**2 + 2*z**2)
- r[1,2] = 2*y*z + 2*x*w
- r[2,0] = 2*x*z+2*y*w
- r[2,1] = 2*y*z - 2*x*w
- r[2,2] = 1 - (2*x**2+2*y**2)
- return r
-
-def rotateAbout(axis, angle):
- ax, ay, az = (axis/norm(axis)).A1
- qx = ax*sin(angle/2.0)
- qy = ay*sin(angle/2.0)
- qz = az*sin(angle/2.0)
- qw = cos(angle/2.0)
- q = mat(array([qx,qy,qz,qw])).T
- return q
-
-def normalizeQuaternion(quat):
- x,y,z,w = quat.A1
- magnitude = sqrt(x*x + y*y + z*z + w*w)
- x = x / magnitude
- y = y / magnitude
- z = z / magnitude
- w = w / magnitude
- quat[0, 0] = x
- quat[1, 0] = y
- quat[2, 0] = z
- quat[3, 0] = w
- return quat
-
-def VTo4x4(V):
- v1, v2, v3 = V.A1
- return mat(array( \
- [[0.0, -v3, v2, -v1], \
- [ v3, 0.0, -v1, -v2], \
- [-v2, v1, 0.0, -v3], \
- [v1, v2, v3, 0.0] ]))
-
-def homogeneous(R,p):
- H = mat(eye(4))
- H[0:3, 0:3] = R
- H[0:3, 3] = p
- return H
-
-def rateOfChange(states, thetime, parameters):
- quat = states[0:4, 0] # Orientation (4)
- pos = states[4:7, 0] # Position (3)
- P = states[7:13, 0] # Momentum (6)
- massI, gravity = parameters
- # Rigid body parts:
- # Forward Kinematic chain:
- H = homogeneous(quatToR(quat), pos) # Forward kinematics
-
- AdjX2 = mat(eye(6)) # The connectionpoint in the real world
- adjAdjX2_1 = adjoint(AdjX2[0:6,0])
- adjAdjX2_2 = adjoint(AdjX2[0:6,1])
- adjAdjX2_3 = adjoint(AdjX2[0:6,2])
- adjAdjX2_4 = adjoint(AdjX2[0:6,3])
- adjAdjX2_5 = adjoint(AdjX2[0:6,4])
- adjAdjX2_6 = adjoint(AdjX2[0:6,5])
- AdjInv2 = Adjoint(H.I)
- M2 = AdjInv2.T * (massI * AdjInv2) # Transfor mass to base frame
- MassMatrix = M2
-
- wrenchGrav2 = mat( zeros((1,6)) )
- wrenchGrav2[0, 0:3] = -cross(gravity, pos).T
- wrenchGrav2[0, 3:6] = gravity.T
-
- Bk = mat( zeros( (6,6) ))
- Bk[0:3, 0:3] = skew(P[0:3, 0])
- Bk[3:6, 0:3] = skew(P[3:6, 0])
- Bk[0:3, 3:6] = skew(P[3:6, 0])
-
- # TODO: do this a cholesky:
- v = np.linalg.solve(MassMatrix, P) # Matrix inverse like thingy !
-
- T2_00 = v # Calculate the relative twist!
- TM2 = T2_00.T * M2
- twistExternal = T2_00 # Twist van het blokje
- TMSum2 = TM2
-
- PDotBodies = mat( zeros( (6,1)) )
- PDotBodies[0,0] = TMSum2 * (adjAdjX2_1 * T2_00)
- PDotBodies[1,0] = TMSum2 * (adjAdjX2_2 * T2_00)
- PDotBodies[2,0] = TMSum2 * (adjAdjX2_3 * T2_00)
- PDotBodies[3,0] = TMSum2 * (adjAdjX2_4 * T2_00)
- PDotBodies[4,0] = TMSum2 * (adjAdjX2_5 * T2_00)
- PDotBodies[5,0] = TMSum2 * (adjAdjX2_6 * T2_00)
-
- PDot = -PDotBodies - Bk * v
- PDot += wrenchGrav2.T
-
- ##### Contact wrench part:
- HB_W = H # Is H-matrix van het blokje
- WrenchB = mat(zeros( (1,6) ))
- for px in [-0.5, 0.5]:
- for py in [-0.5, 0.5]:
- for pz in [-0.5, 0.5]:
- HB1_B = homogeneous(mat(eye(3)), mat([px,py,pz]).T)
- HB1_W = HB_W * HB1_B
- HW1_W = homogeneous(mat(eye(3)), HB1_W[0:3,3])
- HW_W1 = HW1_W.I
- HB_W1 = HW_W1 * HB_W
-
- AdjHB_W1 = Adjoint(HB_W1)
- TB_W1_W1 = AdjHB_W1 * twistExternal
- z = HB1_W[2, 3]
- vx, vy, vz = TB_W1_W1[3:6, 0].A1
- if z < 0:
- # Contact forces:
- Fx = -50.0*vx
- Fy = -50.0*vy
- Fz = -z*50000.0
- else:
- Fx = 0.0
- Fy = 0.0
- Fz = 0.0
- # TODO: reflect impulse
- WrenchW1 = mat([0,0,0,0,0,Fz])
- # Transform it back:
- WrenchB += (AdjHB_W1.T * WrenchW1.T).T
- ##### End of contact wrench
-
- PDot += (WrenchB * AdjInv2).T
-
- # Position and orientation rates:
- QOmega = VTo4x4(v[0:3, 0])
- quatDot = 0.5 * QOmega * quat
- vel = v[3:6, 0]
- posDot = skew(v[0:3]) * pos + vel
- # The rate vector:
- rates = mat(zeros( (13,1) ))
- rates[0:4, 0] = quatDot
- rates[4:7, 0] = posDot
- rates[7:13, 0] = PDot
- return rates
-
-def fWrapper(y, t, parameters):
- y = mat(y).T
- dy = rateOfChange(y, t, parameters)
- return dy.T.A1
-
-def SCIPY(endtime, dt, state, parameters):
- times = np.arange(0.0, endtime, dt)
- y0 = state.T.A1
- res = scipy.integrate.odeint(fWrapper, y0, times, args=(parameters,))
- states = []
- res = res.T
- r,c = res.shape
- for ci in range(0,c):
- states.append(mat(res[:,ci]).T)
- return states
-
-def RK4(endtime, dt, state, parameters):
- t = 0.0
- states = []
- while t < endtime:
- newstate = mat (zeros( state.shape )) # Create a new object
-
- #### Runge Kutta integration:
- k1 = rateOfChange(state, t, parameters)
- k2 = rateOfChange(state + 0.5*dt*k1, t, parameters)
- k3 = rateOfChange(state + 0.5*dt*k1, t, parameters)
- k4 = rateOfChange(state + dt*k3, t, parameters)
- newstate = state + (dt/6.0)*(k1+2*k2+2*k3+k4)
-
- # Normalize quat:
- newstate[0:4, 0] = normalizeQuaternion(newstate[0:4, 0])
- states.append(newstate)
-
- state = newstate
-
- t += dt
- print state[6,0], t, ' ', (t/endtime)*100.0, '%'
- return states
-
-
-def simulate(endtime, dt):
- PInitial = mat( zeros((6,1)) )
- posInitial = mat(array([-1.2, -1.3, 2.8])).T
- quatInitial = rotateAbout(mat(array([0.2, 1.0, 0.4])).T, 0.5)
- # Parameters:
- gravity = mat( array([0,0,-9.81]) ).T
- massI = mat(eye(6)) * 1.01 # Mass matrix
- parameters = (massI, gravity)
-
- # The state vector!
- state = mat(zeros( (13,1) ))
- state[0:4, 0] = quatInitial
- state[4:7, 0] = posInitial
- state[7:13, 0] = PInitial
-
- return SCIPY(endtime, dt, state, parameters)
-
-class W(QGLWidget):
- time = 0.0
- index = 0
- def __init__(self, states, dt, parent=None):
- super(W, self).__init__(parent)
- self.firstRun = True
- self.savePNGS = False
- self.dt = dt
- self.resize(500,500)
- self.states = states
- self.UP()
- t = QTimer(self)
- t.timeout.connect(self.UP)
- t.start(self.dt*1000.0)
-
- def UP(self):
- self.time += self.dt
- if self.states:
- state = self.states[self.index]
- self.Dicequat = state[0:4, 0]
- self.Dicepos = state[4:7, 0]
- self.index += 1
- if self.index == len(self.states):
- self.index = 0
- self.firstRun = False
- # Paint:
- self.update()
- if self.firstRun:
- # Create png images for the movie:
- if self.savePNGS:
- pm = self.renderPixmap()
- pm.save('image'+str(self.index)+'.png')
-
- def initializeGL(self):
- glClearColor(0.0, 0.5, 0.0, 1.0)
- glEnable(GL_DEPTH_TEST)
- glDepthFunc(GL_LESS)
- glShadeModel(GL_SMOOTH)
- def resizeGL(self,w,h):
- glViewport(0, 0, w, h)
- glMatrixMode(GL_PROJECTION)
- glLoadIdentity()
- gluPerspective(45.0, float(w)/float(h), 0.1, 100.0)
- glMatrixMode(GL_MODELVIEW)
- def paintGL(self):
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Clear buffers
- glLoadIdentity() # Reset The View
-
- glLoadIdentity()
- glTranslatef(0.0,-2.0,-10.0) # Move Left And Into The Screen
- glRotatef(-90.0, 1.0, 0.0, 0.0)
- drawFloor(2.0, 0.0)
- drawAxis()
- self.renderText(1.0, 0.0, 0.0, 'X')
- self.renderText(0.0, 1.0, 0.0, 'Y')
- self.renderText(0.0, 0.0, 1.0, 'Z')
-
- self.renderText(0.0,0.0,1.2,str(self.time))
-
- x,y,z = self.Dicepos.A1
- R = quatToR(self.Dicequat)
-
- glTranslatef(x, y, z)
- # Trick to rotate the openGL matrix:
- r = R.A1
- rotR = (r[0], r[3], r[6], 0.0,
- r[1], r[4], r[7], 0.0,
- r[2], r[5], r[8], 0.0,
- 0.0, 0.0, 0.0, 1.0)
- glMultMatrixd(rotR)
-
- drawCube(0.6)
-
-et = 20.0
-dt = 0.04
-print 'starting integration... endtime =', et, ' stepsize =', dt
-t0 = time.time()
-states = simulate(et, dt)
-t1 = time.time()
-print 'That was heavy, it took me ', t1-t0, ' seconds!'
-app = QApplication(sys.argv)
-w = W(states, dt)
-w.show()
-sys.exit(app.exec_())
-
diff -r 6fa41208a3a8 -r fd7d5069734e applications/lab/diagrameditor.py
--- a/applications/lab/diagrameditor.py Wed May 02 07:50:35 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,653 +0,0 @@
-#!/usr/bin/python
-
-from PyQt4.QtGui import *
-from PyQt4.QtCore import *
-import sys
-import xml.dom.minidom as md
-import xml
-
-"""
- Author: Windel Bouwman
- Year: 2012
- Description: This script implements a diagram editor.
- run with python 3.x as:
- $ python [thisfile.py]
-"""
-
-class ArrowHead(QGraphicsPathItem):
- def __init__(self, parent):
- super(ArrowHead, self).__init__(parent)
- arrowPath = QPainterPath(QPointF(0.0, 0.0))
- arrowPath.lineTo(-6.0, 10.0)
- arrowPath.lineTo(6.0, 10.0)
- arrowPath.lineTo(0.0, 0.0)
- self.setPath(arrowPath)
- pen = QPen(Qt.blue, 2)
- self.setPen(pen)
- self.setBrush(QBrush(pen.color()))
- self.myshape = QPainterPath()
-
-class Connection(QGraphicsPathItem):
- """ Implementation of a connection between blocks """
- def __init__(self, fromPort, toPort):
- super(Connection, self).__init__()
- self.pos1 = None
- self.pos2 = None
- self.fromPort = None
- self.toPort = None
- self.setFlag(self.ItemIsSelectable, True)
- self.setFlag(self.ItemClipsToShape, True)
- self.pen = QPen(Qt.blue, 2)
- self.pen.setCapStyle(Qt.RoundCap)
- self.setPen(self.pen)
- self.arrowhead = ArrowHead(self)
- self.vias = []
- self.setFromPort(fromPort)
- self.setToPort(toPort)
- def mouseDoubleClickEvent(self, event):
- pos = event.scenePos()
- pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2]
- idx = 0
- tidx = 0
- for p1, p2 in zip(pts[0:-1], pts[1:]):
- l1 = QLineF(p1, p2)
- l2 = QLineF(p1, pos)
- l3 = QLineF(pos, p2)
- d = l2.length() + l3.length() - l1.length()
- if d < 5:
- tidx = idx
- idx += 1
- self.addHandle(pos, tidx)
-
- def addHandle(self, pos, idx=None):
- hi = HandleItem(self)
- if idx:
- self.vias.insert(idx, hi)
- else:
- self.vias.append(hi)
- def callback(p):
- self.updateLineStukken()
- return p
- hi.posChangeCallbacks.append(callback)
- hi.setPos(pos)
- self.updateLineStukken()
-
- def setFromPort(self, fromPort):
- if self.fromPort:
- self.fromPort.posCallbacks.remove(self.setBeginPos)
- self.fromPort.connection = None
- self.fromPort = fromPort
- if self.fromPort:
- self.fromPort.connection = self
- self.setBeginPos(fromPort.scenePos())
- self.fromPort.posCallbacks.append(self.setBeginPos)
- def setToPort(self, toPort):
- if self.toPort:
- self.toPort.posCallbacks.remove(self.setEndPos)
- self.toPort.connection = None
- self.toPort = toPort
- if self.toPort:
- self.setEndPos(toPort.scenePos())
- self.toPort.connection = self
- self.toPort.posCallbacks.append(self.setEndPos)
- def releasePorts(self):
- self.setFromPort(None)
- self.setToPort(None)
- def setBeginPos(self, pos1):
- self.pos1 = pos1
- self.updateLineStukken()
- def setEndPos(self, endpos):
- self.pos2 = endpos
- self.updateLineStukken()
- def itemChange(self, change, value):
- if change == self.ItemSelectedHasChanged:
- for via in self.vias:
- via.setVisible(value)
- return super(Connection, self).itemChange(change, value)
- def shape(self):
- return self.myshape
- def updateLineStukken(self):
- """
- This algorithm determines the optimal routing of all signals.
- TODO: implement nice automatic line router
- """
- if self.pos1 is None or self.pos2 is None:
- return
- pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2]
- self.arrowhead.setPos(self.pos2)
- if pts[-1].x() < pts[-2].x():
- self.arrowhead.setRotation(-90)
- else:
- self.arrowhead.setRotation(90)
- path = QPainterPath(pts[0])
- for pt in pts[1:]:
- path.lineTo(pt)
- self.setPath(path)
- """ Create a shape outline using the path stroker """
- s = super(Connection, self).shape()
- pps = QPainterPathStroker()
- pps.setWidth(10)
- self.myshape = pps.createStroke(s).simplified()
-
-class ParameterDialog(QDialog):
- def __init__(self, block, parent = None):
- super(ParameterDialog, self).__init__(parent)
- self.block = block
- self.button = QPushButton('Ok', self)
- l = QGridLayout(self)
- l.addWidget(QLabel('Name:', self), 0, 0)
- self.nameEdit = QLineEdit(self.block.name)
- l.addWidget(self.nameEdit, 0, 1)
- l.addWidget(QLabel('Code:', self), 1, 0)
- self.codeEdit = QTextEdit(self)
- self.codeEdit.setPlainText(self.block.code)
- l.addWidget(self.codeEdit, 1, 1)
- l.addWidget(self.button, 2, 0, 1, 2)
- self.button.clicked.connect(self.OK)
- def OK(self):
- self.block.setName(self.nameEdit.text())
- self.block.code = self.codeEdit.toPlainText()
- self.close()
-
-class PortItem(QGraphicsPathItem):
- """ Represents a port to a subsystem """
- def __init__(self, name, block, direction):
- super(PortItem, self).__init__(block)
- self.connection = None
- path = QPainterPath()
- d = 10.0
- if direction == 'input':
- path.moveTo(-d, -d)
- path.lineTo(0.0, 0.0)
- path.lineTo(-d, d)
- else:
- path.moveTo(0.0, -d)
- path.lineTo(d, 0.0)
- path.lineTo(0.0, d)
- self.setPath(path)
- self.direction = direction
- self.block = block
- self.setCursor(QCursor(Qt.CrossCursor))
- pen = QPen(Qt.blue, 2)
- pen.setCapStyle(Qt.RoundCap)
- self.setPen(pen)
- self.name = name
- self.textItem = QGraphicsTextItem(name, self)
- self.setName(name)
- self.posCallbacks = []
- self.setFlag(self.ItemSendsScenePositionChanges, True)
- def setName(self, name):
- self.name = name
- self.textItem.setPlainText(name)
- rect = self.textItem.boundingRect()
- lw, lh = rect.width(), rect.height()
- if self.direction == 'input':
- lx = 3
- else:
- lx = -3 - lw
- self.textItem.setPos(lx, -lh / 2)
- def itemChange(self, change, value):
- if change == self.ItemScenePositionHasChanged:
- for cb in self.posCallbacks:
- cb(value)
- return value
- return super(PortItem, self).itemChange(change, value)
- def mousePressEvent(self, event):
- if self.direction == 'output':
- self.scene().startConnection(self)
-
-class OutputPort(PortItem):
- # TODO: create a subclass OR make a member porttype
- pass
-
-# Block part:
-class HandleItem(QGraphicsEllipseItem):
- """ A handle that can be moved by the mouse """
- def __init__(self, parent=None):
- dx = 13.0
- super(HandleItem, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent)
- self.posChangeCallbacks = []
- self.setBrush(QBrush(Qt.white))
- self.setFlag(self.ItemSendsScenePositionChanges, True)
- self.setFlag(self.ItemIsMovable, True)
- self.setVisible(False)
- self.setCursor(QCursor(Qt.SizeFDiagCursor))
- def mouseMoveEvent(self, event):
- """ Move function without moving the other selected elements """
- p = self.mapToParent(event.pos())
- self.setPos(p)
- def mySetPos(self, p):
- # TODO: use this instead of itemChange?
- self.setPos(p)
- def itemChange(self, change, value):
- if change == self.ItemPositionChange:
- for cb in self.posChangeCallbacks:
- res = cb(value)
- if res:
- value = res
- return value
- # Call superclass method:
- return super(HandleItem, self).itemChange(change, value)
-
-def uniqify(name, names):
- newname = name
- i = 1
- while newname in names:
- newname = name + str(i)
- i += 1
- return newname
-
-class BlockItem(QGraphicsRectItem):
- """
- Represents a block in the diagram
- Has an x and y and width and height
- width and height can only be adjusted with a tip in the lower right corner.
-
- - in and output ports
- - parameters
- - description
- """
- def __init__(self, name='Untitled', parent=None):
- super(BlockItem, self).__init__(parent)
- # Properties of the rectangle:
- self.setPen(QPen(Qt.blue, 2))
- self.setBrush(QBrush(Qt.lightGray))
- self.setFlags(self.ItemIsSelectable | self.ItemIsMovable)
- self.setFlag(self.ItemSendsScenePositionChanges, True)
- self.setCursor(QCursor(Qt.PointingHandCursor))
- self.label = QGraphicsTextItem(name, self)
- self.name = name
- self.code = ''
- # Create corner for resize:
- self.sizer = HandleItem(self)
- self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback
- button = QPushButton('+in')
- button.clicked.connect(self.newInputPort)
- self.buttonItemAddInput = QGraphicsProxyWidget(self)
- self.buttonItemAddInput.setWidget(button)
- self.buttonItemAddInput.setVisible(False)
- button = QPushButton('+out')
- button.clicked.connect(self.newOutputPort)
- self.buttonItemAddOutput = QGraphicsProxyWidget(self)
- self.buttonItemAddOutput.setWidget(button)
- self.buttonItemAddOutput.setVisible(False)
-
- # Inputs and outputs of the block:
- self.inputs = []
- self.outputs = []
- # Update size:
- self.sizer.mySetPos(QPointF(60, 40)) # This is a better resize function
- def editParameters(self):
- pd = ParameterDialog(self, self.window())
- pd.exec_()
- def mouseDoubleClickEvent(self, event):
- self.editParameters()
- def newInputPort(self):
- names = [i.name for i in self.inputs + self.outputs]
- self.addInput(PortItem(uniqify('in', names), self, 'input'))
- def newOutputPort(self):
- names = [i.name for i in self.inputs + self.outputs]
- self.addOutput(PortItem(uniqify('out', names), self, 'output'))
- def setName(self, name):
- self.name = name
- self.label.setPlainText(name)
- def addInput(self, i):
- self.inputs.append(i)
- self.updateSize()
- def addOutput(self, o):
- self.outputs.append(o)
- self.updateSize()
-
- def contextMenuEvent(self, event):
- menu = QMenu()
- pa = menu.addAction('Parameters')
- pa.triggered.connect(self.editParameters)
- menu.exec_(event.screenPos())
- def itemChange(self, change, value):
- if change == self.ItemSelectedHasChanged:
- self.sizer.setVisible(value)
- self.buttonItemAddInput.setVisible(value)
- self.buttonItemAddOutput.setVisible(value)
- return super(BlockItem, self).itemChange(change, value)
-
- def updateSize(self):
- rect = self.rect()
- h, w = rect.height(), rect.width()
- self.buttonItemAddInput.setPos(0, h + 4)
- self.buttonItemAddOutput.setPos(w+10, h+4)
- if len(self.inputs) == 1:
- self.inputs[0].setPos(0.0, h / 2)
- elif len(self.inputs) > 1:
- y = 15
- dy = (h - 30) / (len(self.inputs) - 1)
- for inp in self.inputs:
- inp.setPos(0.0, y)
- y += dy
- if len(self.outputs) == 1:
- self.outputs[0].setPos(w, h / 2)
- elif len(self.outputs) > 1:
- y = 15
- dy = (h - 30) / (len(self.outputs) - 1)
- for outp in self.outputs:
- outp.setPos(w, y)
- y += dy
-
- def changeSize(self, p):
- """ Resize block function """
- w, h = p.x(), p.y()
- # Limit the block size:
- if h < 20:
- h = 20
- if w < 40:
- w = 40
- self.setRect(0.0, 0.0, w, h)
- # center label:
- rect = self.label.boundingRect()
- lw, lh = rect.width(), rect.height()
- lx = (w - lw) / 2
- ly = (h - lh) / 2
- self.label.setPos(lx, ly)
- # Update port positions:
- self.updateSize()
- return QPointF(w, h)
-
-class EditorGraphicsView(QGraphicsView):
- def __init__(self, scene, parent=None):
- QGraphicsView.__init__(self, scene, parent)
- self.setDragMode(QGraphicsView.RubberBandDrag)
- def wheelEvent(self, event):
- pos = event.pos()
- posbefore = self.mapToScene(pos)
- degrees = event.delta() / 8.0
- sx = (100.0 + degrees) / 100.0
- self.scale(sx, sx)
- event.accept()
- def dragEnterEvent(self, event):
- if event.mimeData().hasFormat('component/name'):
- event.accept()
- def dragMoveEvent(self, event):
- if event.mimeData().hasFormat('component/name'):
- event.accept()
- def dropEvent(self, event):
- if event.mimeData().hasFormat('component/name'):
- name = bytes(event.mimeData().data('component/name')).decode()
- pos = self.mapToScene(event.pos())
- self.scene().addNewBlock(pos, name)
-
-class LibraryModel(QStandardItemModel):
- def __init__(self, parent=None):
- QStandardItemModel.__init__(self, parent)
- def mimeTypes(self):
- return ['component/name']
- def mimeData(self, idxs):
- mimedata = QMimeData()
- for idx in idxs:
- if idx.isValid():
- txt = self.data(idx, Qt.DisplayRole) # python 3
- mimedata.setData('component/name', txt)
- return mimedata
-
-class DiagramScene(QGraphicsScene):
- """ Save and load and deletion of item"""
- def __init__(self, parent=None):
- super(DiagramScene, self).__init__(parent)
- self.startedConnection = None
-
- def saveDiagram(self, filename):
- items = self.items()
- blocks = [item for item in items if type(item) is BlockItem]
- connections = [item for item in items if type(item) is Connection]
-
- doc = md.Document()
- modelElement = doc.createElement('system')
- doc.appendChild(modelElement)
- for block in blocks:
- blockElement = doc.createElement("block")
- x, y = block.scenePos().x(), block.scenePos().y()
- rect = block.rect()
- w, h = rect.width(), rect.height()
- blockElement.setAttribute("name", block.name)
- blockElement.setAttribute("x", str(int(x)))
- blockElement.setAttribute("y", str(int(y)))
- blockElement.setAttribute("width", str(int(w)))
- blockElement.setAttribute("height", str(int(h)))
- codeNode = doc.createCDATASection(block.code)
- codeElement = doc.createElement('code')
- codeElement.appendChild(codeNode)
- blockElement.appendChild(codeElement)
- for inp in block.inputs:
- portElement = doc.createElement("input")
- portElement.setAttribute("name", inp.name)
- blockElement.appendChild(portElement)
- for outp in block.outputs:
- portElement = doc.createElement("output")
- portElement.setAttribute("name", outp.name)
- blockElement.appendChild(portElement)
- modelElement.appendChild(blockElement)
- for connection in connections:
- connectionElement = doc.createElement("connection")
- fromPort = connection.fromPort.name
- toPort = connection.toPort.name
- fromBlock = connection.fromPort.block.name
- toBlock = connection.toPort.block.name
- connectionElement.setAttribute("fromBlock", fromBlock)
- connectionElement.setAttribute("fromPort", fromPort)
- connectionElement.setAttribute("toBlock", toBlock)
- connectionElement.setAttribute("toPort", toPort)
- for via in connection.vias:
- viaElement = doc.createElement('via')
- viaElement.setAttribute('x', str(int(via.x())))
- viaElement.setAttribute('y', str(int(via.y())))
- connectionElement.appendChild(viaElement)
- modelElement.appendChild(connectionElement)
- with open(filename, 'w') as f:
- f.write(doc.toprettyxml())
-
- def loadDiagram(self, filename):
- try:
- doc = md.parse(filename)
- except IOError as e:
- print('{0} not found'.format(filename))
- return
- except xml.parsers.expat.ExpatError as e:
- print('{0}'.format(e))
- return
- sysElements = doc.getElementsByTagName('system')
- blockElements = doc.getElementsByTagName('block')
- for sysElement in sysElements:
- blockElements = sysElement.getElementsByTagName('block')
- for blockElement in blockElements:
- x = float(blockElement.getAttribute('x'))
- y = float(blockElement.getAttribute('y'))
- w = float(blockElement.getAttribute('width'))
- h = float(blockElement.getAttribute('height'))
- name = blockElement.getAttribute('name')
- block = BlockItem(name)
- self.addItem(block)
- block.setPos(x, y)
- block.sizer.setPos(w, h)
- codeElements = blockElement.getElementsByTagName('code')
- if codeElements:
- cn = codeElements[0].childNodes
- cdatas = [cd for cd in cn if type(cd) is md.CDATASection]
- if len(cdatas) > 0:
- block.code = cdatas[0].data
- # Load ports:
- portElements = blockElement.getElementsByTagName('input')
- for portElement in portElements:
- name = portElement.getAttribute('name')
- inp = PortItem(name, block, 'input')
- block.addInput(inp)
- portElements = blockElement.getElementsByTagName('output')
- for portElement in portElements:
- name = portElement.getAttribute('name')
- outp = PortItem(name, block, 'output')
- block.addOutput(outp)
- connectionElements = sysElement.getElementsByTagName('connection')
- for connectionElement in connectionElements:
- fromBlock = connectionElement.getAttribute('fromBlock')
- fromPort = connectionElement.getAttribute('fromPort')
- toBlock = connectionElement.getAttribute('toBlock')
- toPort = connectionElement.getAttribute('toPort')
- viaElements = connectionElement.getElementsByTagName('via')
- fromPort = self.findPort(fromBlock, fromPort)
- toPort = self.findPort(toBlock, toPort)
- connection = Connection(fromPort, toPort)
- for viaElement in viaElements:
- x = int(viaElement.getAttribute('x'))
- y = int(viaElement.getAttribute('y'))
- connection.addHandle(QPointF(x, y))
- self.addItem(connection)
- def findPort(self, blockname, portname):
- items = self.items()
- blocks = [item for item in items if type(item) is BlockItem]
- for block in [b for b in blocks if b.name == blockname]:
- for port in block.inputs + block.outputs:
- if port.name == portname:
- return port
- def addNewBlock(self, pos, name):
- blocknames = [item.name for item in self.items() if type(item) is BlockItem]
- b1 = BlockItem(uniqify(name, blocknames))
- b1.setPos(pos)
- self.addItem(b1)
- def mouseMoveEvent(self, event):
- if self.startedConnection:
- pos = event.scenePos()
- self.startedConnection.setEndPos(pos)
- super(DiagramScene, self).mouseMoveEvent(event)
- def mouseReleaseEvent(self, event):
- if self.startedConnection:
- items = self.items(event.scenePos())
- for item in items:
- if type(item) is PortItem:
- self.startedConnection.setToPort(item)
- self.startedConnection = None
- return
- self.deleteItem(self.startedConnection)
- self.startedConnection = None
- super(DiagramScene, self).mouseReleaseEvent(event)
- def startConnection(self, port):
- self.startedConnection = Connection(port, None)
- pos = port.scenePos()
- self.startedConnection.setEndPos(pos)
- self.addItem(self.startedConnection)
- def deleteItem(self, item=None):
- if item:
- if type(item) is BlockItem:
- for p in item.inputs + item.outputs:
- if p.connection:
- self.deleteItem(p.connection)
- self.removeItem(item)
- elif type(item) is Connection:
- item.releasePorts()
- self.removeItem(item)
- else:
- # No item was supplied, try to delete all currently selected items:
- items = self.selectedItems()
- connections = [item for item in items if type(item) is Connection]
- blocks = [item for item in items if type(item) is BlockItem]
- for item in connections + blocks:
- self.deleteItem(item)
-
-class DiagramEditor(QWidget):
- def __init__(self, parent=None):
- QWidget.__init__(self, parent)
-
- # Widget layout and child widgets:
- self.horizontalLayout = QHBoxLayout(self)
- self.diagramScene = DiagramScene(self)
- self.loadDiagram = self.diagramScene.loadDiagram
- self.diagramView = EditorGraphicsView(self.diagramScene, self)
- self.horizontalLayout.addWidget(self.diagramView)
-
- testShortcut = QShortcut(QKeySequence("F12"), self)
- testShortcut.activated.connect(self.test)
- delShort = QShortcut(QKeySequence.Delete, self)
- delShort.activated.connect(self.diagramScene.deleteItem)
-
- def test(self):
- self.diagramView.rotate(30)
- self.zoomAll()
- def save(self):
- self.diagramScene.saveDiagram('diagram2.usd')
- def load(self):
- filename = QFileDialog.getOpenFileName(self)
- self.diagramScene.loadDiagram(filename)
- def zoomAll(self):
- """ zoom to fit all items """
- rect = self.diagramScene.itemsBoundingRect()
- self.diagramView.fitInView(rect, Qt.KeepAspectRatio)
-
-class LibraryWidget(QListView):
- def __init__(self):
- super(LibraryWidget, self).__init__(None)
- self.libraryModel = LibraryModel(self)
- self.libraryModel.setColumnCount(1)
- # Create an icon with an icon:
- pixmap = QPixmap(60, 60)
- pixmap.fill()
- painter = QPainter(pixmap)
- painter.fillRect(10, 10, 40, 40, Qt.blue)
- painter.setBrush(Qt.yellow)
- painter.drawEllipse(20, 20, 20, 20)
- painter.end()
-
- # Fill library:
- self.libItems = []
- self.libItems.append( QStandardItem(QIcon(pixmap), 'Block') )
- self.libItems.append( QStandardItem(QIcon(pixmap), 'Uber Unit') )
- self.libItems.append( QStandardItem(QIcon(pixmap), 'Device') )
- for i in self.libItems:
- self.libraryModel.appendRow(i)
- self.setModel(self.libraryModel)
- self.setViewMode(self.IconMode)
- self.setDragDropMode(self.DragOnly)
-
-class Main(QMainWindow):
- def __init__(self):
- super(Main, self).__init__(None)
- self.editor = DiagramEditor()
- self.setCentralWidget(self.editor)
- self.setWindowTitle("Diagram editor")
- toolbar = self.addToolBar('Tools')
-
- saveAction = QAction('Save', self)
- saveAction.setShortcuts(QKeySequence.Save)
- saveAction.triggered.connect(self.editor.save)
- toolbar.addAction(saveAction)
- openAction = QAction('Open', self)
- openAction.setShortcuts(QKeySequence.Open)
- openAction.triggered.connect(self.editor.load)
- toolbar.addAction(openAction)
- fullScreenAction = QAction('Full screen', self)
- fullScreenAction.setShortcuts(QKeySequence("F11"))
- fullScreenAction.triggered.connect(self.toggleFullScreen)
- toolbar.addAction(fullScreenAction)
- zoomAction = QAction('Fit in view', self)
- zoomAction.setShortcuts(QKeySequence('F8'))
- zoomAction.triggered.connect(self.editor.zoomAll)
- toolbar.addAction(zoomAction)
-
- self.library = LibraryWidget()
- libraryDock = QDockWidget('Library', self)
- libraryDock.setWidget(self.library)
- self.addDockWidget(Qt.LeftDockWidgetArea, libraryDock)
-
- self.editor.loadDiagram('diagram2.usd')
-
- def toggleFullScreen(self):
- self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
- self.editor.zoomAll()
-
-if __name__ == '__main__':
- if sys.version_info.major != 3:
- print('Please use python 3.x')
- sys.exit(1)
-
- app = QApplication(sys.argv)
- main = Main()
- main.show()
- main.resize(700, 500)
- main.editor.zoomAll()
- app.exec_()
-
diff -r 6fa41208a3a8 -r fd7d5069734e cos/bochsrc.txt
--- a/cos/bochsrc.txt Wed May 02 07:50:35 2012 +0200
+++ b/cos/bochsrc.txt Sun Oct 07 16:56:50 2012 +0200
@@ -1,7 +1,7 @@
display_library: x, options="gui_debug" # use GTK debugger gui
romimage: file=$BXSHARE/BIOS-bochs-latest
-cpu: count=1, ips=500000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def"
-cpuid: mmx=1, sep=1, sse=sse4_2, xapic=1, aes=1, movbe=1, xsave=1, cpuid_limit_winnt=0
+cpu: count=1, ips=1500000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def"
+cpuid: mmx=1, sep=1, sse=sse4_2, aes=1, movbe=1, xsave=1
memory: guest=128, host=256
megs: 48
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/__init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/__init__.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,2 @@
+# File to make this directory a package.
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/assembler.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/assembler.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,354 @@
+"""
+ Assembler code generation functions
+"""
+
+from .errors import Error
+
+modrm = {'rax': 0, 'rbx': 1}
+
+# Table 3.1 of the intel manual:
+# use REX.W on the table below:
+regs64 = {'rax': 0,'rcx':1,'rdx':2,'rbx':3,'rsp':4,'rbp':5,'rsi':6,'rdi':7,'r8':0,'r9':1,'r10':2,'r11':3,'r12':4,'r13':5,'r14':6,'r15':7}
+regs32 = {'eax': 0, 'ecx':1, 'edx':2, 'ebx': 3, 'esp': 4, 'ebp': 5, 'esi':6, 'edi':7}
+regs8 = {'al':0,'cl':1,'dl':2,'bl':3,'ah':4,'ch':5,'dh':6,'bh':7}
+
+# Calculation of the rexb bit:
+rexbit = {'rax': 0, 'rcx':0, 'rdx':0, 'rbx': 0, 'rsp': 0, 'rbp': 0, 'rsi':0, 'rdi':0,'r8':1,'r9':1,'r10':1,'r11':1,'r12':1,'r13':1,'r14':1,'r15':1}
+
+# Helper functions:
+def imm64(x):
+ """ represent 64 bits integer in little endian 8 bytes"""
+ if x < 0:
+ x = x + (1 << 64)
+ x = x & 0xFFFFFFFFFFFFFFFF
+ return [ (x >> (p*8)) & 0xFF for p in range(8) ]
+
+def imm32(x):
+ """ represent 32 bits integer in little endian 4 bytes"""
+ if x < 0:
+ x = x + (1 << 32)
+ x = x & 0xFFFFFFFF
+ return [ (x >> (p*8)) & 0xFF for p in range(4) ]
+
+def imm8(x):
+ if x < 0:
+ x = x + (1 << 8)
+ x = x & 0xFF
+ return [ x ]
+
+def modrm(mod=0, rm=0, reg=0):
+ """ Construct the modrm byte from its components """
+ assert(mod <= 3)
+ assert(rm <= 7)
+ assert(reg <= 7)
+ return (mod << 6) | (reg << 3) | rm
+
+def rex(w=0, r=0, x=0, b=0):
+ """ Create a REX prefix byte """
+ assert(w <= 1)
+ assert(r <= 1)
+ assert(x <= 1)
+ assert(b <= 1)
+ return 0x40 | (w<<3) | (r<<2) | (x<<1) | b
+
+def sib(ss=0, index=0, base=0):
+ assert(ss <= 3)
+ assert(index <= 7)
+ assert(base <= 7)
+ return (ss << 6) | (index << 3) | base
+
+tttn = {'L':0xc,'G':0xf,'NE':0x5,'GE':0xd,'LE':0xe, 'E':0x4}
+
+# Actual instructions:
+def nearjump(distance, condition=None):
+ """ jmp imm32 """
+ lim = (1<<30)
+ if abs(distance) > lim:
+ Error('near jump cannot jump over more than {0} bytes'.format(lim))
+ if condition:
+ if distance < 0:
+ distance -= 6 # Skip own instruction
+ opcode = 0x80 | tttn[condition] # Jcc imm32
+ return [0x0F, opcode] + imm32(distance)
+ else:
+ if distance < 0:
+ distance -= 5 # Skip own instruction
+ return [ 0xE9 ] + imm32(distance)
+
+def shortjump(distance, condition=None):
+ """ jmp imm8 """
+ lim = 118
+ if abs(distance) > lim:
+ Error('short jump cannot jump over more than {0} bytes'.format(lim))
+ if distance < 0:
+ distance -= 2 # Skip own instruction
+ if condition:
+ opcode = 0x70 | tttn[condition] # Jcc rel8
+ else:
+ opcode = 0xeb # jmp rel8
+ return [opcode] + imm8(distance)
+
+# Helper that determines jump type:
+def reljump(distance):
+ if abs(distance) < 110:
+ return shortjump(distance)
+ else:
+ return nearjump(distance)
+
+def push(reg):
+ if reg in regs64:
+ if rexbit[reg] == 1:
+ return [0x41, 0x50 + regs64[reg]]
+ else:
+ return [0x50 + regs64[reg]]
+ else:
+ Error('push for {0} not implemented'.format(reg))
+
+def pop(reg):
+ if reg in regs64:
+ if rexbit[reg] == 1:
+ rexprefix = rex(b=1)
+ opcode = 0x58 + regs64[reg]
+ return [rexprefix, opcode]
+ else:
+ opcode = 0x58 + regs64[reg]
+ return [ opcode ]
+ else:
+ Error('pop for {0} not implemented'.format(reg))
+
+def INT(number):
+ opcode = 0xcd
+ return [opcode] + imm8(number)
+
+def syscall():
+ return [0x0F, 0x05]
+
+def call(distance):
+ if type(distance) is int:
+ return [0xe8]+imm32(distance)
+ elif type(distance) is str and distance in regs64:
+ reg = distance
+ opcode = 0xFF # 0xFF /2 == call r/m64
+ mod_rm = modrm(mod=3, reg=2, rm=regs64[reg])
+ if rexbit[reg] == 1:
+ rexprefix = rex(b=rexbit[reg])
+ return [rexprefix, opcode, mod_rm]
+ else:
+ return [opcode, mod_rm]
+ else:
+ Error('Cannot call to {0}'.format(distance))
+
+def ret():
+ return [ 0xc3 ]
+
+def increg64(reg):
+ assert(reg in regs64)
+ rexprefix = rex(w=1, b=rexbit[reg])
+ opcode = 0xff
+ mod_rm = modrm(mod=3, rm=regs64[reg])
+ return [rexprefix, opcode, mod_rm]
+
+def prepost8(r8, rm8):
+ assert(r8 in regs8)
+ pre = []
+ if type(rm8) is list:
+ # TODO: merge mem access with prepost for 64 bits
+ if len(rm8) == 1:
+ base, = rm8
+ if type(base) is str and base in regs64:
+ assert(not base in ['rbp', 'rsp', 'r12', 'r13'])
+ mod_rm = modrm(mod=0, rm=regs64[base], reg=regs8[r8])
+ if rexbit[base] == 1:
+ pre.append(rex(b=1))
+ post = [mod_rm]
+ else:
+ Error('One arg of type {0} not implemented'.format(base))
+ elif len(rm8) == 2:
+ base, offset = rm8
+ assert(type(offset) is int)
+ assert(base in regs64)
+
+ if base == 'rsp' or base == 'r12':
+ Error('Cannot use rsp or r12 as base yet')
+ if rexbit[base] == 1:
+ pre.append( rex(b=1) )
+ mod_rm = modrm(mod=1, rm=regs64[base], reg=regs8[r8])
+ post = [mod_rm] + imm8(offset)
+ else:
+ Error('not supporting prepost8 with list len {0}'.format(len(rm8)))
+ else:
+ Error('Not supporting move with reg8 {0}'.format(r8))
+ return pre, post
+
+def prepost(r64, rm64):
+ assert(r64 in regs64)
+ if type(rm64) is list:
+ if len(rm64) == 3:
+ base, index, disp = rm64
+ assert(base in regs64)
+ assert(index in regs64)
+ assert(type(disp) is int)
+ # Assert that no special cases are used:
+ # TODO: swap base and index to avoid special cases
+ # TODO: exploit special cases and make better code
+ assert(index != 'rsp')
+
+ rexprefix = rex(w=1, r=rexbit[r64], x=rexbit[index], b=rexbit[base])
+ # mod=1 and rm=4 indicates a SIB byte: [--][--]+imm8
+ mod_rm = modrm(mod=1, rm=4, reg=regs64[r64])
+ si_b = sib(ss=0, index=regs64[index], base=regs64[base])
+ return [rexprefix], [mod_rm, si_b] + imm8(disp)
+ elif len(rm64) == 2:
+ base, offset = rm64
+ assert(type(offset) is int)
+ if base == 'RIP':
+ # RIP pointer relative addressing mode!
+ rexprefix = rex(w=1, r=rexbit[r64])
+ mod_rm = modrm(mod=0, rm=5, reg=regs64[r64])
+ return [rexprefix], [mod_rm] + imm32(offset)
+ else:
+ assert(base in regs64)
+
+ if base == 'rsp' or base == 'r12':
+ # extended function that uses SIB byte
+ rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base])
+ # rm=4 indicates a SIB byte follows
+ mod_rm = modrm(mod=1, rm=4, reg=regs64[r64])
+ # index=4 indicates that index is not used
+ si_b = sib(ss=0, index=4, base=regs64[base])
+ return [rexprefix], [mod_rm, si_b] + imm8(offset)
+ else:
+ rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base])
+ mod_rm = modrm(mod=1, rm=regs64[base], reg=regs64[r64])
+ return [rexprefix], [mod_rm] + imm8(offset)
+ elif len(rm64) == 1:
+ offset = rm64[0]
+ if type(offset) is int:
+ rexprefix = rex(w=1, r=rexbit[r64])
+ mod_rm = modrm(mod=0, rm=4,reg=regs64[r64])
+ si_b = sib(ss=0, index=4,base=5) # 0x25
+ return [rexprefix], [mod_rm, si_b] + imm32(offset)
+ else:
+ Error('Memory reference of type {0} not implemented'.format(offset))
+ else:
+ Error('Memory reference not implemented')
+ elif rm64 in regs64:
+ rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[rm64])
+ mod_rm = modrm(3, rm=regs64[rm64], reg=regs64[r64])
+ return [rexprefix], [mod_rm]
+
+def leareg64(rega, m):
+ opcode = 0x8d # lea r64, m
+ pre, post = prepost(rega, m)
+ return pre + [opcode] + post
+
+def mov(rega, regb):
+ if type(regb) is int:
+ pre = [rex(w=1, b=rexbit[rega])]
+ opcode = 0xb8 + regs64[rega]
+ post = imm64(regb)
+ elif type(regb) is str:
+ if regb in regs64:
+ opcode = 0x89 # mov r/m64, r64
+ pre, post = prepost(regb, rega)
+ elif regb in regs8:
+ opcode = 0x88 # mov r/m8, r8
+ pre, post = prepost8(regb, rega)
+ else:
+ Error('Unknown register {0}'.format(regb))
+ elif type(rega) is str:
+ if rega in regs64:
+ opcode = 0x8b # mov r64, r/m64
+ pre, post = prepost(rega, regb)
+ else:
+ Error('Unknown register {0}'.format(rega))
+ else:
+ Error('Move of this kind {0}, {1} not implemented'.format(rega, regb))
+ return pre + [opcode] + post
+
+def xorreg64(rega, regb):
+ rexprefix = rex(w=1, r=rexbit[regb], b=rexbit[rega])
+ opcode = 0x31 # XOR r/m64, r64
+ # Alternative is 0x33 XOR r64, r/m64
+ mod_rm = modrm(3, rm=regs64[rega], reg=regs64[regb])
+ return [rexprefix, opcode, mod_rm]
+
+# integer arithmatic:
+def addreg64(rega, regb):
+ if regb in regs64:
+ pre, post = prepost(regb, rega)
+ opcode = 0x01 # ADD r/m64, r64
+ return pre + [opcode] + post
+ elif type(regb) is int:
+ if regb < 100:
+ rexprefix = rex(w=1, b=rexbit[rega])
+ opcode = 0x83 # add r/m, imm8
+ mod_rm = modrm(3, rm=regs64[rega], reg=0)
+ return [rexprefix, opcode, mod_rm]+imm8(regb)
+ elif regb < (1<<31):
+ rexprefix = rex(w=1, b=rexbit[rega])
+ opcode = 0x81 # add r/m64, imm32
+ mod_rm = modrm(3, rm=regs64[rega], reg=0)
+ return [rexprefix, opcode, mod_rm]+imm32(regb)
+ else:
+ Error('Constant value too large!')
+ else:
+ Error('unknown second operand!'.format(regb))
+
+def subreg64(rega, regb):
+ if regb in regs64:
+ pre, post = prepost(regb, rega)
+ opcode = 0x29 # SUB r/m64, r64
+ return pre + [opcode] + post
+ elif type(regb) is int:
+ if regb < 100:
+ rexprefix = rex(w=1, b=rexbit[rega])
+ opcode = 0x83 # sub r/m, imm8
+ mod_rm = modrm(3, rm=regs64[rega], reg=5)
+ return [rexprefix, opcode, mod_rm]+imm8(regb)
+ elif regb < (1<<31):
+ rexprefix = rex(w=1, b=rexbit[rega])
+ opcode = 0x81 # sub r/m64, imm32
+ mod_rm = modrm(3, rm=regs64[rega], reg=5)
+ return [rexprefix, opcode, mod_rm]+imm32(regb)
+ else:
+ Error('Constant value too large!')
+
+ else:
+ Error('unknown second operand!'.format(regb))
+
+def idivreg64(reg):
+ rexprefix = rex(w=1, b=rexbit[reg])
+ opcode = 0xf7 # IDIV r/m64
+ mod_rm = modrm(3, rm=regs64[reg], reg=7)
+ return [rexprefix, opcode, mod_rm]
+
+def imulreg64_rax(reg):
+ rexprefix = rex(w=1, b=rexbit[reg])
+ opcode = 0xf7 # IMUL r/m64
+ mod_rm = modrm(3, rm=regs64[reg], reg=5)
+ return [rexprefix, opcode, mod_rm]
+
+def imulreg64(rega, regb):
+ pre, post = prepost(rega, regb)
+ opcode = 0x0f # IMUL r64, r/m64
+ opcode2 = 0xaf
+ return pre + [opcode, opcode2] + post
+
+def cmpreg64(rega, regb):
+ if regb in regs64:
+ pre, post = prepost(regb, rega)
+ opcode = 0x39 # CMP r/m64, r64
+ return pre + [opcode] + post
+ elif type(regb) is int:
+ rexprefix = rex(w=1, b=rexbit[rega])
+ opcode = 0x83 # CMP r/m64, imm8
+ mod_rm = modrm(3, rm=regs64[rega], reg=7)
+ return [rexprefix, opcode, mod_rm] + imm8(regb)
+
+ else:
+ Error('not implemented cmp64')
+
+# Mapping that maps string names to the right functions:
+opcodes = {'mov':(mov,2), 'lea':(leareg64,2), 'int':(INT,1), 'syscall':(syscall,0)}
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/builtin.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/builtin.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,10 @@
+from .nodes import *
+
+boolean = BaseType('boolean', 8) # Choose: 1 or 8 bytes?
+integer = BaseType('integer', 8)
+real = BaseType('real', 8)
+char = BaseType('char', 1)
+void = BaseType('void', 0)
+
+chr_func = BuiltinProcedure('chr', ProcedureType([Parameter('value', 'x', integer)], char))
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/codegenerator.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/codegenerator.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,487 @@
+"""
+ Code generation for 64 bits intel processors
+"""
+
+from .nodes import *
+from .errors import Error
+from .builtin import real, integer, boolean, char
+from .assembler import *
+
+class CodeGenerator:
+ def __init__(self):
+ self.strings = []
+ self.initialize()
+ def initialize(self):
+ # Register descriptors:
+ self.freeregs = 'r8,r9,r10,r11,r12,r13,r14,r15'.split(',')
+ self.usedregs = []
+ # Members to accumulate the result into:
+ # The result is an image of bytecode and global variable space.
+ # Global variables a referenced by RIP relative addressing.
+ self.image = []
+ self.rip = 0 # The current instruction pointer location.
+ # TODO: backpatch list here?
+
+ # Functions to modify the code image
+ def addCode(self, code):
+ assert(type(code) is list)
+ self.image += code
+ self.rip += len(code)
+ def fixCode(self, position, code):
+ self.image[position:position+len(code)] = code
+ def align(self, b):
+ while (self.rip % b) != 0:
+ self.addCode([0])
+
+ def saveAllRegisters(self):
+ regs = list(self.usedregs.keys())
+ for reg in regs:
+ code += self.saveRegister(reg)
+
+ def saveRegister(self, reg):
+ code = []
+ if reg in self.usedregs.keys():
+ code.append('mov {0}, {1}'.format(self.usedregs[reg], reg))
+ del self.usedregs[reg]
+ self.freeregs.append(reg)
+
+ def getreg(self, node):
+ """ acquire a working register for a certain node."""
+ # Temporary register bypass action:
+ if len(self.freeregs) > 0:
+ reg = self.freeregs.pop(0)
+ self.usedregs.append(reg)
+ else:
+ Error('No more free regs')
+ node.reg = reg
+
+ def freereg(self, node):
+ reg = node.reg
+ node.reg = None
+ self.freeregs.append(reg)
+ self.usedregs.remove(reg)
+
+ # Helpers to load and retrieve designated objects:
+ def storeRegInDesignator(self, reg, designator):
+ assert(type(reg) is str)
+ assert(type(designator) is Designator)
+ if len(designator.selectors) > 0:
+ self.gencode( designator ) # Load the pointer into some register
+ self.addCode( mov([designator.reg, 0x0], reg) )
+ self.freereg( designator )
+ else:
+ if designator.obj.isLocal:
+ # Relative from rbp register
+ mem = ['rbp', designator.obj.offset]
+ self.addCode( mov(mem, reg) )
+ else:
+ # Relative from RIP after move
+ self.addCode( mov(['RIP', 0x0], reg) )
+ self.fixCode(self.rip - 4, imm32(designator.obj.offset - self.rip) )
+
+ # Code generation functions:
+ def genexprcode(self, node):
+ """
+ Generate code for expressions!
+ Recursively evaluates, and ensures a register contains the answer.
+ register is an integer register or a floating point reg
+ """
+ if isinstance(node, Binop):
+ """ Handle a binary operation (two arguments) of some kind """
+ self.genexprcode(node.a)
+ self.genexprcode(node.b)
+
+ if node.op == 'mod':
+ assert(node.typ.isType(integer))
+ self.addCode(mov('rax', node.a.reg))
+ self.addCode(xorreg64('rdx', 'rdx')) # Extend divided number with zeros
+ self.addCode(idivreg64(node.b.reg)) # divide rdx:rax with reg
+ node.reg = node.a.reg
+ self.freereg(node.b) # give up register that contains b
+ self.addCode(mov(node.reg, 'rdx')) # move remainder into result
+ elif node.op == 'div':
+ assert(node.typ.isType(integer))
+ self.addCode(mov('rax', node.a.reg))
+ self.addCode(xorreg64('rdx', 'rdx')) # Extend divided number with zeros
+ self.addCode(idivreg64(node.b.reg)) # divide rdx:rax with reg
+ node.reg = node.a.reg
+ self.freereg(node.b) # give up register that contains b
+ self.addCode(mov(node.reg, 'rax')) # move result into reg
+ elif node.op == '*':
+ if node.typ.isType(integer):
+ self.addCode(imulreg64(node.a.reg, node.b.reg))
+ node.reg = node.a.reg
+ self.freereg(node.b)
+ else:
+ Error('{0} for * not implemented'.format(node.typ))
+ elif node.op == '+':
+ if node.typ.isType(integer):
+ self.addCode(addreg64(node.a.reg, node.b.reg))
+ node.reg = node.a.reg
+ self.freereg(node.b)
+ else:
+ Error('{0} for + not implemented'.format(node.typ))
+ elif node.op == '-':
+ if node.typ.isType(integer):
+ self.addCode(subreg64(node.a.reg, node.b.reg))
+ node.reg = node.a.reg
+ self.freereg(node.b)
+ else:
+ Error('{0} for - not implemented'.format(node.typ))
+ else:
+ Error('Unknown Binop {0}'.format(node.op))
+
+ elif type(node) is Unop:
+ if node.op == 'INTTOREAL':
+ self.genexprcode(node.a)
+ node.reg = node.a.reg
+ # TODO use 'FILD' instruction
+ freg = 12
+ code.append('Unop inttoreal TODO')
+ elif node.op == 'ABS':
+ if isType(node.typ, real):
+ code = [0xD9, 0xE1] # st(0) = fabs st(0)
+ Error('ABS error integer')
+ elif isType(node.typ, integer):
+ code = []
+ Error('ABS error integer')
+ else:
+ Error('ABS error')
+ else:
+ Error('Unknown Unop {0}'.format(node.op))
+
+ elif isinstance(node, Designator):
+ # dereference, array index. Make sure that the result comes into a register
+ if len(node.selectors) > 0:
+ self.gencode(node) # Load the pointer into some register
+
+ # Now we can access the object at location '[node.reg]':
+ if node.typ.isType(integer):
+ self.addCode( mov(node.reg, [node.reg, 0x0]) )
+ else:
+ Error('Only integer types implemented')
+ else:
+ # No selectors, load variable directly
+ if node.obj.typ.isType(integer):
+ if type(node.obj) is Constant:
+ self.genexprcode(node.obj)
+ node.reg = node.obj.reg
+ else:
+ self.getreg(node)
+ # Get a register to store the integer value
+ if node.obj.isLocal:
+ # relative to rbp:
+ self.addCode( mov(node.reg, ['rbp', node.obj.offset]) )
+ else:
+ self.addCode(mov(node.reg, ['RIP', 0x0]))
+ self.fixCode(self.rip-4, imm32(node.obj.offset - self.rip))
+ else:
+ Error('Cannot load variable type {0}'.format(node.typ))
+
+ elif isinstance(node, Relop):
+ # Create a boolean from operands
+ # TODO create an alternative for expressions used as conditions.
+ self.genexprcode(node.a)
+ self.genexprcode(node.b)
+
+ if node.a.typ.isType(integer):
+ instructions = {'<': 'L', '>': 'G', '<>': 'NE', '>=': 'GE', '<=': 'LE', '=':'E'}
+ if not node.relop in instructions.keys():
+ Error('Unimplemented relop: '+str(node.relop))
+ instr = instructions[node.relop]
+
+ node.reg = node.a.reg
+ self.addCode( cmpreg64(node.a.reg, node.b.reg) )
+ self.addCode( shortjump(0x0, condition=instr) ) # jump over 0 code and jmp
+ fixloc1 = self.rip - 1
+ rip1 = self.rip
+ self.addCode( xorreg64(node.reg, node.reg) )
+ self.addCode( shortjump(0x0) ) # Jump over 1 code
+ fixloc2 = self.rip - 1
+ self.fixCode(fixloc1, imm8(self.rip - rip1))
+ rip2 = self.rip
+ self.addCode( xorreg64(node.reg, node.reg) )
+ self.addCode( increg64(node.reg) )
+ self.fixCode(fixloc2, imm8(self.rip - rip2))
+
+ self.freereg(node.b)
+ else:
+ Error('Relop not implemented for {0}'.format(node.a.typ))
+
+ elif type(node) is Constant:
+ if node.typ.isType(integer):
+ self.getreg(node)
+ self.addCode(mov(node.reg, node.value))
+ elif node.typ.isType(real):
+ code += self.getreg(node)
+ Error('TODO: get real reg')
+ # TODO: get a fixed point reg, and load the variable in there
+ else:
+ Error('Howto generate code for {0}?'.format(node))
+
+ elif type(node) is ProcedureCall:
+ if type(node.proc.obj) is BuiltinProcedure:
+ # Handle builtin procedures different, these not always call
+ # a function, but generate code.
+ bi = node.proc.obj
+ if bi.name == 'chr':
+ arg = node.args[0]
+ self.genexprcode(arg)
+ # Store character in full width register:
+ # TODO: store in char only register
+ node.reg = arg.reg
+ else:
+ Error('Unknown builtin function {0}'.format(bi.name))
+ else:
+ # Use generic procedure call first
+ self.gencode(node)
+ # Retrieve result:
+ if node.typ.isType(integer):
+ # Store result!
+ self.getreg(node)
+ self.addCode( mov(node.reg, 'rax') )
+ else:
+ Error('Return type not supported {0}'.format(node.typ))
+ else:
+ Error('Cannot generate expression code for: {0}'.format(node))
+
+ def gencode(self, node):
+ """ Code generation function for AST nodes """
+ if isinstance(node, Module):
+ # for all imports make a list of pointer to the actual procedures:
+ for imp in node.imports:
+ imp.offset = self.rip
+ self.addCode( [0x0]*8 )
+ # global variable storage allocation
+ variables = node.symtable.getAllLocal(Variable)
+ for var in variables:
+ var.isLocal = False
+ var.offset = self.rip
+ self.addCode( [0x00] * var.typ.size ) # TODO initial values here?
+ self.align(8)
+ # TODO: mark end of data and start of code inside image
+ # TODO: round data to page size to enable protection by loader.
+ # Procedure code generation:
+ procedures = node.symtable.getAllLocal(Procedure)
+ node.procs = procedures
+ for proc in procedures:
+ self.gencode(proc)
+ # Module init code:
+ node.initcodeentry = self.rip
+ self.gencode(node.initcode)
+ self.addCode( ret() )
+ # TODO: how to return from module init code? far return??
+
+ elif type(node) is Procedure:
+ # calculate offsets for local variables and parameters
+ # Variable location relative to 'rbp' register
+ variables = node.symtable.getAllLocal(Variable)
+ offset = 0
+ paramoffset = 16
+ for var in variables:
+ var.isLocal = True
+ if not var.isParameter:
+ offset += var.typ.size
+ # Offset is negative of rbp in stack frame
+ var.offset = -offset
+ node.framesize = offset
+ # Calculate offsets of parameters relative to rbp register
+ for par in reversed(node.typ.parameters):
+ pvar = node.symtable.getLocal(Variable, par.name)
+ pvar.offset = paramoffset
+ paramoffset += pvar.typ.size
+
+ # code generation
+ node.entrypoint = self.rip
+ self.addCode(push('rbp'))
+ self.addCode(mov('rbp', 'rsp')) # Setup the base pointer
+ self.addCode(subreg64('rsp', node.framesize)) # reserve space for locals
+ self.gencode(node.block)
+ if node.retexpr:
+ if node.retexpr.typ.isType(integer):
+ self.genexprcode(node.retexpr)
+ self.addCode( mov('rax', node.retexpr.reg) )
+ self.freereg(node.retexpr)
+ else:
+ Error('Cannot return this kind yet {0}'.format(node.retexpr.typ))
+ self.addCode( addreg64('rsp', node.framesize) )
+ self.addCode( pop('rbp') )
+ self.addCode( ret() )
+ assert(len(self.usedregs) == 0)
+
+ elif isinstance(node, StatementSequence):
+ for s in node.statements:
+ self.gencode(s)
+
+ elif type(node) is ProcedureCall:
+ # Prepare parameters on the stack:
+ stacksize = 0
+ assert(len(node.args) == len(node.proc.typ.parameters))
+ for arg, param in zip(node.args, node.proc.typ.parameters):
+
+ if param.kind == 'value':
+ self.genexprcode(arg)
+ self.addCode( push(arg.reg) )
+ self.freereg( arg )
+ stacksize += 8
+ else:
+ Error('Parameter kind other than value')
+
+ # Calculate address using designator
+ if type(node.proc.obj) is Procedure:
+ self.addCode( call(0x0) )
+ self.fixCode( self.rip - 4, imm32(node.proc.obj.entrypoint - self.rip))
+ elif type(node.proc.obj) is ImportedSymbol:
+ # Load the entry point of the import table
+ self.getreg(node.proc.obj)
+ # Load the address of the procedure:
+ self.addCode( mov(node.proc.obj.reg, ['RIP', 0x0]) )
+ self.fixCode( self.rip - 4, imm32(node.proc.obj.offset - self.rip) )
+ # Call to the address in register:
+ self.addCode( call(node.proc.obj.reg) )
+ # Free register that holds the address of the object
+ self.freereg( node.proc.obj )
+ elif type(node.proc.obj) is BuiltinProcedure:
+ if node.proc.obj.name == 'chr':
+ print('int to char')
+ else:
+ Error('Unknown builtin function {0}'.format(node.proc.obj.name))
+ else:
+ Error('Cannot call designator of type {0}'.format(node.proc.obj))
+
+ # Restore stack (pop all arguments of):
+ self.addCode(addreg64('rsp', stacksize))
+
+ elif type(node) is Assignment:
+ if node.lval.typ.isType(integer):
+ # TODO if node.rval is Constant of some datatype, move it to mem directly
+ self.genexprcode(node.rval) # Calculate the value that has to be stored.
+ self.storeRegInDesignator(node.rval.reg, node.lval)
+ self.freereg(node.rval)
+ else:
+ Error('Assignments of other types not implemented')
+ # TODO if left and right are designators, do some sort of memcpy.
+
+ elif type(node) is IfStatement:
+ self.genexprcode(node.condition)
+ self.addCode( cmpreg64(node.condition.reg, 1) )
+ self.freereg(node.condition)
+ if node.falsestatement:
+ # If with else clause
+ self.addCode( nearjump(0x0, condition='NE') ) # if Not Equal jump to false
+ rip1 = self.rip
+ fixloc1 = self.rip - 4
+ self.gencode(node.truestatement)
+ self.addCode( nearjump( 0x0 ) ) # jump over false code
+ fixloc2 = self.rip - 4
+ self.fixCode(fixloc1, imm32(self.rip - rip1))
+ rip2 = self.rip
+ self.gencode(node.falsestatement)
+ self.fixCode(fixloc2, imm32(self.rip - rip2))
+ else:
+ # If without else clause
+ self.addCode( nearjump(0x0, condition='NE') ) # if Not Equal jump to false
+ rip1 = self.rip
+ fixloc1 = self.rip - 4
+ self.gencode(node.truestatement)
+ self.fixCode(fixloc1, imm32(self.rip - rip1)) # Fixup near jump over true code.
+
+ elif isinstance(node, WhileStatement):
+ rip1 = self.rip # Store the start of the while loop
+ self.genexprcode(node.condition)
+ self.addCode( cmpreg64(node.condition.reg, 1) ) # Test condition for true-ness
+ self.freereg(node.condition)
+ self.addCode( nearjump(0x0, condition='NE') ) # If Not Equal jump over while code AND jump back (fix later)
+ fixloc1 = self.rip - 4
+ rip2 = self.rip
+ self.gencode(node.dostatements)
+ self.addCode( nearjump(0x0) ) # JMP to condition, fix exact jump position below
+ fixloc2 = self.rip - 4
+ rip3 = self.rip # end of while loop
+ self.fixCode(fixloc2, imm32(rip1 - rip3)) # Fixup jump to start of while loop
+ self.fixCode(fixloc1, imm32(rip3 - rip2)) # Fixup jump out of while loop
+
+ elif type(node) is ForStatement:
+ # Initial load of iterator variable:
+ self.genexprcode(node.begin)
+ self.genexprcode(node.end)
+ # TODO: link reg with variable so that a register is used instead of a variable
+ iterreg = node.begin.reg # Get the register used for the loop
+ #self.addCode(cmpreg64(iterreg, node.endvalue))
+ rip1 = self.rip
+ self.gencode(node.statements)
+ #self.loadDesignatorInReg(node.
+ #self.addCode( addreg64(node.variable, node.increment) )
+ self.addCode(nearjump(0x0))
+ fixloc1 = self.rip - 4
+ rip2 = self.rip
+ self.fixCode(fixloc1, imm32(rip1 - rip2))
+
+ self.freereg(node.begin) # Release register used in loop
+ self.freereg(node.end)
+ Error('No implementation of FOR statement')
+
+ elif type(node) is AsmCode:
+ def processOperand(op):
+ if type(op) is list:
+ if type(op[0]) is Variable:
+ var = op[0]
+ if var.isLocal:
+ return ['rbp', var.offset]
+ else:
+ Error('Can only use local variables in inline assembler')
+ return op
+ for asmline in node.asmcode:
+ opcode, operands = asmline
+ operands = [processOperand(opx) for opx in operands]
+ print('assembling', opcode, *operands)
+ func,nargs = opcodes[opcode]
+ code = func(*operands)
+ self.addCode(code)
+
+ elif isinstance(node, EmptyStatement):
+ pass
+
+
+ elif type(node) is StringConstant:
+ self.strings.append(node)
+ self.data.append(node.value) # Add string to the data section
+
+ elif type(node) is Designator:
+ if len(node.selectors) > 0:
+ self.getreg(node)
+ # Load starting address
+ if node.obj.isLocal:
+ self.addCode( leareg64(node.reg, ['rbp', node.obj.offset]) )
+ else:
+ # Global variables need to be relocated...
+ self.addCode(leareg64(node.reg, ['RIP', 0]))
+ self.fixCode(self.rip - 4, imm32(node.obj.offset - self.rip))
+ # Loop over all designators..
+ for selector in node.selectors:
+ if type(selector) is Index:
+ # Deref an array index
+ self.genexprcode(selector.index)
+ self.getreg(selector)
+ self.addCode( mov(selector.reg, selector.typ.elementType.size) )
+ self.addCode( imulreg64(selector.reg, selector.index.reg ) )
+ self.freereg(selector.index)
+ self.addCode(addreg64(node.reg, selector.reg))
+ self.freereg(selector)
+ elif type(selector) is Field:
+ print('Field')
+ Error('Field not implemented')
+ else:
+ Error('Unknown selector')
+ else:
+ Error('Can only gencode for designator with selectors')
+
+ else:
+ print('not generating code for {0}'.format(node))
+
+ def generatecode(self, ast):
+ """ code generation front end """
+ self.initialize()
+ self.gencode(ast)
+ ast.image = self.image
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/compiler.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/compiler.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,53 @@
+import hashlib
+# Import compiler components:
+from . import lexer
+from .parser import Parser
+from .codegenerator import CodeGenerator
+from .nodes import ExportedSymbol
+from .errors import CompilerException
+
+class Compiler:
+ versie = '0.9.3'
+
+ def __repr__(self):
+ return 'LCFOS compiler {0}'.format(self.versie)
+
+ def generateSignature(self, src):
+ return hashlib.md5(bytes(src,encoding='ascii')).hexdigest()
+
+ def compilesource(self, src):
+ """ Front end that handles the stages: """
+ self.errorlist = []
+ # Pass 1: parsing and type checking
+ tokens = lexer.tokenize(src) # Lexical stage
+ p = Parser(tokens)
+ try:
+ ast = p.parseModule() # Parse a module
+ except CompilerException as e:
+ p.errorlist.append( (e.row, e.col, e.msg) )
+ if len(p.errorlist) > 0:
+ self.errorlist = p.errorlist
+ return
+ # Pass 2: code generation
+ CodeGenerator().generatecode(ast)
+ # Attach a signature:
+ ast.signature = self.generateSignature(src)
+ # Generate exported symbols:
+ ast.exports = []
+ for proc in ast.procs:
+ if proc.public:
+ sym = ExportedSymbol(proc.name, proc.typ)
+ sym.imageoffset = proc.entrypoint
+ ast.exports.append(sym)
+ return ast
+
+ def compileProject(self, project):
+ mods = []
+ for fname in project.files:
+ print('Compiling {0}...'.format(fname))
+ source = project.loadProjectFile(fname)
+ mod = self.compilesource(source)
+ mods.append(mod)
+ return mods
+
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/display.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/display.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,21 @@
+from .nodes import *
+
+def printNode(node, indent=0):
+ """
+ Print visitor
+ all printing goes in here
+ """
+ print(' '*indent+str(node))
+ if type(node) is Procedure:
+ print(' '*indent+' PARAMETERS:')
+ for p in node.parameters:
+ printNode(p, indent+4)
+ if node.block:
+ print(' '*indent+' CODE:')
+ printNode(node.block, indent+4)
+ elif type(node) is Module:
+ print(node.symtable)
+ printNode(node.initcode, indent+2)
+ else:
+ for c in node.getChildren():
+ printNode(c, indent+2)
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/errors.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/errors.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,47 @@
+""" Error handling routines """
+
+class CompilerException(Exception):
+ def __init__(self, msg, row=0, col=0, filename=None):
+ self.msg = msg
+ self.row = row
+ self.col = col
+ self.filename = filename
+ def __repr__(self):
+ return self.msg
+ def __str__(self):
+ return self.msg
+
+class ErrorNode:
+ def __init__(self, row, col, msg):
+ self.row, self.col = row,col
+ self.msg = msg
+
+def Error(msg, node=None):
+ if node is None:
+ raise CompilerException(msg)
+ else:
+ raise CompilerException(msg, node.row, node.col)
+
+def printError(source, e):
+ def printLine(row, txt):
+ print(str(row)+':'+txt)
+ if e.row == 0:
+ print('Error: {0}'.format(e.msg))
+ else:
+ lines = source.split('\n')
+ prerow = e.row - 3
+ if prerow < 1:
+ prerow = 1
+ afterrow = e.row + 3
+ if afterrow > len(lines):
+ afterrow = len(lines)
+
+ # print preceding source lines:
+ for r in range(prerow, e.row):
+ printLine(r, lines[r-1])
+ # print source line containing error:
+ printLine(e.row, lines[e.row-1])
+ print(' '*(len(str(e.row)+':')+e.col-1) + '^ Error: {0}'.format(e.msg))
+ # print trailing source line:
+ for r in range(e.row+1, afterrow+1):
+ printLine(r, lines[r-1])
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/lexer.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/lexer.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,71 @@
+import collections
+import re
+from .errors import CompilerException
+
+"""
+ Lexical analyzer part. Splits the input character stream into tokens.
+"""
+
+# Token is used in the lexical analyzer:
+Token = collections.namedtuple('Token', 'typ val row col')
+
+keywords = ['and', 'array', 'begin', 'by', 'case', 'const', 'div', 'do', \
+ 'else', 'elsif', 'end', 'false', 'for', 'if', 'import', 'in', 'is', \
+ 'mod', 'module', 'nil', 'not', 'of', 'or', 'pointer', 'procedure', \
+ 'record', 'repeat', 'return', 'then', 'to', 'true', 'type', 'until', 'var', \
+ 'while', 'asm' ]
+
+def tokenize(s):
+ """
+ Tokenizer, generates an iterator that
+ returns tokens!
+
+ This GREAT example was taken from python re doc page!
+ """
+ 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'{.*}'),
+ ('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)
+ 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 != 'SKIP':
+ 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]
+ yield Token(typ, val, line, mo.start()-line_start)
+ pos = mo.end()
+ mo = gettok(s, pos)
+ if pos != len(s):
+ col = pos - line_start
+ raise CompilerException('Unexpected character {0}'.format(s[pos]), line, col)
+ yield Token('END', '', line, 0)
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/modules.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/modules.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,193 @@
+import struct
+from .errors import Error
+from .nodes import *
+from .builtin import integer, real, char, boolean, void
+import os.path
+
+"""
+ File format for compiled modules.
+ * [11] magic identifier
+ * [STR] mod name
+ * [STR] signature, a md5 signature of the module.
+ * [I32] size of code
+ * code image
+ * [I32] entrypoint for initcode
+ * imported modules
+ ** [I32] num of imported modules
+ *** [STR] name of module
+ *** signature of the module
+ *** [I32] offset in the process image where the interface symbols must be placed
+ * public interface
+ ** [I32] num of interface elements
+ *** [STR] proc name
+ *** [I32] offset in code image
+ *** [type] return type
+ *** [I32] number of parameters
+ **** parameter
+ ***** parameter kind
+ ***** parameter name
+ ***** parameter type
+"""
+
+MAGIC = b'LCFOSMODC'
+
+loadedModules = []
+
+def loadModule(modname):
+ """ returns a Module object specified by a name """
+ # Check if the module was already loaded:
+ for mod in loadedModules:
+ if mod.name == modname:
+ return mod
+
+ # Try to load the module from file:
+ srcfilename = modname + '.mod'
+ binfilename = modname + '.bin'
+ sourceExists = os.path.exists(srcfilename)
+ if os.path.exists(binfilename):
+ if sourceExists:
+ compileModule()
+ else:
+ return loadModuleFromFile(binfilename)
+ else:
+ Error("Cannot load module '{0}'!".format(modname))
+
+def loadModuleFromFile(filename):
+ f = open(filename, 'rb')
+ magic = f.read(len(MAGIC))
+ assert(magic == MAGIC)
+
+ # Helper functions:
+ def readI32():
+ int32, = struct.unpack(' 0:
+ operands.append( parseOperand() )
+ n = n - 1
+ while n > 0:
+ self.Consume(',')
+ operands.append(parseOperand())
+ n = n - 1
+ return operands
+ self.Consume('asm')
+ asmcode = []
+ while self.token.typ != 'end':
+ opcode = parseOpcode()
+ func, numargs = assembler.opcodes[opcode]
+ operands = parseOperands(numargs)
+ asmcode.append( (opcode, operands) )
+ #print('opcode', opcode, operands)
+ self.Consume('end')
+ return AsmCode(asmcode)
+
+ def parseStatement(self):
+ try:
+ # Determine statement type based on the pending token:
+ if self.token.typ == 'if':
+ return self.parseIfStatement()
+ elif self.token.typ == 'case':
+ return self.parseCaseStatement()
+ elif self.token.typ == 'while':
+ return self.parseWhileStatement()
+ elif self.token.typ == 'repeat':
+ return self.parseRepeatStatement()
+ elif self.token.typ == 'for':
+ return self.parseForStatement()
+ elif self.token.typ == 'asm':
+ return self.parseAsmcode()
+ elif self.token.typ == 'ID':
+ # Assignment or procedure call
+ designator = self.parseDesignator()
+ if self.token.typ == '(' and type(designator.typ) is ProcedureType:
+ return self.parseProcedureCall(designator)
+ elif self.token.typ == ':=':
+ return self.parseAssignment(designator)
+ else:
+ self.Error('Unknown statement following designator: {0}'.format(self.token))
+ else:
+ # TODO: return empty statement??:
+ return EmptyStatement()
+ self.Error('Unknown statement {0}'.format(self.token))
+ except CompilerException as e:
+ print(e)
+ self.errorlist.append( (e.row, e.col, e.msg))
+ # Do error recovery by skipping all tokens until next ; or end
+ while not (self.token.typ == ';' or self.token.typ == 'end'):
+ self.Consume(self.token.typ)
+ return EmptyStatement()
+
+ def parseStatementSequence(self):
+ """ Sequence of statements seperated by ';' """
+ statements = [ self.parseStatement() ]
+ while self.hasConsumed(';'):
+ statements.append( self.parseStatement() )
+ return StatementSequence( statements )
+
+ # Parsing expressions:
+ """
+ grammar of expressions:
+ expression = SimpleExpression [ reloperator SimpleExpression ]
+ reloperator = '=' | '<=' | '>=' | '<>'
+ Simpleexpression = [ '+' | '-' ] term { addoperator term }
+ addoperator = '+' | '-' | 'or'
+ term = factor { muloperator factor }
+ muloperator = '*' | '/' | 'div' | 'mod' | 'and'
+ factor = number | nil | true | false | "(" expression ")" |
+ designator [ actualparameters ] | 'not' factor
+ """
+ def parseExpression(self):
+ """ The connector between the boolean and expression domain """
+ expr = self.parseSimpleExpression()
+ if self.token.typ in ['>=','<=','<','>','<>','=']:
+ relop = self.Consume()
+ expr2 = self.parseSimpleExpression()
+ # Automatic type convert to reals:
+ if isType(expr.typ, real) and isType(expr2.typ, integer):
+ expr2 = Unop(expr2, 'INTTOREAL', real)
+ if isType(expr2.typ, real) and isType(expr.typ, integer):
+ expr = Unop(expr, 'INTTOREAL', real)
+ # Type check:
+ if not isType(expr.typ, expr2.typ):
+ self.Error('Type mismatch in relop')
+ if isType(expr.typ, real) and relop in ['<>', '=']:
+ self.Error('Cannot check real values for equality')
+
+ expr = Relop(expr, relop, expr2, boolean)
+ return expr
+
+ # Parsing arithmatic expressions:
+ def parseTerm(self):
+ a = self.parseFactor()
+ while self.token.typ in ['*', '/', 'mod', 'div', 'and']:
+ loc = self.getLocation()
+ op = self.Consume()
+ b = self.parseTerm()
+ # Type determination and checking:
+ if op in ['mod', 'div']:
+ if not isType(a.typ, integer):
+ self.Error('First operand should be integer, not {0}'.format(a.typ))
+ if not isType(b.typ, integer):
+ self.Error('Second operand should be integer, not {0}'.format(b.typ))
+ typ = integer
+ elif op == '*':
+ if isType(a.typ, integer) and isType(b.typ, integer):
+ typ = integer
+ elif isType(a.typ, real) or isType(b.typ, real):
+ if isType(a.typ, integer):
+ # Automatic type cast
+ a = Unop(a, 'INTTOREAL', real)
+ if isType(b.typ, integer):
+ b = Unop(b, 'INTTOREAL', real)
+ if not isType(a.typ, real):
+ self.Error('first operand must be a real!')
+ if not isType(b.typ, real):
+ self.Error('second operand must be a real!')
+ typ = real
+ else:
+ self.Error('Unknown operands for multiply: {0}, {1}'.format(a, b))
+ elif op == '/':
+ # Division always yields a real result, for integer division use div
+ if isType(a.typ, integer):
+ # Automatic type cast
+ a = Unop(a, 'INTTOREAL', real)
+ if isType(b.typ, integer):
+ b = Unop(b, 'INTTOREAL', real)
+ if not isType(a.typ, real):
+ self.Error('first operand must be a real!')
+ if not isType(b.typ, real):
+ self.Error('second operand must be a real!')
+ typ = real
+ elif op == 'and':
+ if not isType(a.typ, boolean):
+ self.Error('First operand of and must be boolean')
+ if not isType(b.typ, boolean):
+ self.Error('Second operand of and must be boolean')
+ typ = boolean
+ else:
+ self.Error('Unknown operand {0}'.format(op))
+
+ a = self.setLocation(Binop(a, op, b, typ), loc)
+ return a
+
+ def parseFactor(self):
+ if self.hasConsumed('('):
+ e = self.parseExpression()
+ self.Consume(')')
+ return e
+ elif self.token.typ == 'NUMBER':
+ loc = self.getLocation()
+ val = self.Consume('NUMBER')
+ return self.setLocation(Constant(val, integer), loc)
+ elif self.token.typ == 'REAL':
+ loc = self.getLocation()
+ val = self.Consume('REAL')
+ return self.setLocation(Constant(val, real), loc)
+ elif self.token.typ == 'CHAR':
+ val = self.Consume('CHAR')
+ return Constant(val, char)
+ elif self.token.typ == 'STRING':
+ txt = self.Consume('STRING')
+ return StringConstant(txt)
+ elif self.token.typ in ['true', 'false']:
+ val = self.Consume()
+ val = True if val == 'true' else False
+ return Constant(val, boolean)
+ elif self.hasConsumed('nil'):
+ return Constant(0, NilType())
+ elif self.hasConsumed('not'):
+ f = self.parseFactor()
+ if not isType(f.typ, boolean):
+ self.Error('argument of boolean negation must be boolean type')
+ return Unop(f, 'not', boolean)
+ elif self.token.typ == 'ID':
+ designator = self.parseDesignator()
+ # TODO: handle functions different here?
+ if self.token.typ == '(' and type(designator.typ) is ProcedureType:
+ return self.parseProcedureCall(designator)
+ else:
+ return designator
+ else:
+ self.Error('Expected NUMBER, ID or ( expr ), got'+str(self.token))
+
+ def parseSimpleExpression(self):
+ """ Arithmatic expression """
+ if self.token.typ in ['+', '-']:
+ # Handle the unary minus
+ op = self.Consume()
+ a = self.parseTerm()
+ typ = a.typ
+ if not isType(typ,real) and not isType(typ, integer):
+ self.Error('Unary minus or plus can be only applied to real or integers')
+ if op == '-':
+ a = Unop(a, op, typ)
+ else:
+ a = self.parseTerm()
+ while self.token.typ in ['+', '-', 'or']:
+ loc = self.getLocation()
+ op = self.Consume()
+ b = self.parseTerm()
+ if op in ['+', '-']:
+ if isType(a.typ, real) or isType(b.typ, real):
+ typ = real
+ if isType(a.typ, integer):
+ # Automatic type cast
+ a = Unop(a, 'INTTOREAL', real)
+ if not isType(a.typ, real):
+ self.Error('first operand must be a real!')
+ if isType(b.typ, integer):
+ b = Unop(b, 'INTTOREAL', real)
+ if not isType(b.typ, real):
+ self.Error('second operand must be a real!')
+ elif isType(a.typ, integer) and isType(b.typ, integer):
+ typ = integer
+ else:
+ self.Error('Invalid types {0} and {1}'.format(a.typ, b.typ))
+ elif op == 'or':
+ if not isType(a.typ, boolean):
+ self.Error('first operand must be boolean for or operation')
+ if not isType(b.typ, boolean):
+ self.Error('second operand must be boolean for or operation')
+ typ = boolean
+ else:
+ self.Error('Unknown operand {0}'.format(op))
+ a = self.setLocation(Binop(a, op, b, typ), loc)
+ return a
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/compiler/symboltable.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/compiler/symboltable.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,80 @@
+from .nodes import *
+from .errors import Error
+
+class SymbolTable:
+ """
+ Symbol table for a current scope.
+ It has functions:
+ - hasname for checking for a name in current scope or above
+ - addSymbol to add an object
+ """
+ def __init__(self, parent=None):
+ self.parent = parent
+ self.syms = {}
+
+ def __repr__(self):
+ return 'Symboltable with {0} symbols\n'.format(len(self.syms))
+
+ def printTable(self, indent=0):
+ for name in self.syms:
+ print(self.syms[name])
+
+ def getAllLocal(self, cls):
+ """ Get all local objects of a specific type """
+ r = []
+ for key in self.syms.keys():
+ sym = self.syms[key]
+ if issubclass(type(sym), cls):
+ r.append(sym)
+ return r
+
+ def getLocal(self, cls, name):
+ if name in self.syms.keys():
+ sym = self.syms[name]
+ if isinstance(sym, cls):
+ return sym
+ else:
+ Error('Wrong type found')
+ else:
+ Error('Symbol not found')
+
+ # Retrieving of specific classes of items:
+ def get(self, cls, name):
+ if self.hasSymbol(name):
+ sym = self.getSymbol(name)
+ if issubclass(type(sym), cls):
+ return sym
+ raise SymbolException('type {0} undefined'.format(typename))
+
+ def has(self, cls, name):
+ if self.hasSymbol(name):
+ sym = self.getSymbol(name)
+ if issubclass(type(sym), cls):
+ return True
+ return False
+
+ # Adding and retrieving of symbols in general:
+ def addSymbol(self, sym):
+ if sym.name in self.syms.keys():
+ raise Exception('Symbol "{0}" redefined'.format(sym.name))
+ else:
+ self.syms[sym.name] = sym
+
+ def getSymbol(self, name):
+ if name in self.syms.keys():
+ return self.syms[name]
+ else:
+ if self.parent:
+ return self.parent.getSymbol(name)
+ else:
+ Error('Symbol "{0}" undeclared!'.format(name))
+
+ def hasSymbol(self, name):
+ if name in self.syms.keys():
+ return True
+ else:
+ if self.parent:
+ return self.parent.hasSymbol(name)
+ else:
+ return False
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/data/lcfos.png
Binary file python/ide/data/lcfos.png has changed
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/ide/__init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/ide/__init__.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,2 @@
+# Package
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/ide/astviewer.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/ide/astviewer.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,36 @@
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+def astToNamedElement(astNode, parentNode):
+ """ Helper to convert and AST tree to NamedElement tree: """
+ item = QStandardItem(str(astNode))
+ item.setData(astNode)
+ parentNode.appendRow(item)
+ for c in astNode.getChildren():
+ astToNamedElement(c, item)
+
+# The actual widget:
+class AstViewer(QTreeView):
+ sigNodeSelected = pyqtSignal(object)
+ def __init__(self, parent=None):
+ super(AstViewer, self).__init__(parent)
+ self.setHeaderHidden(True)
+ self.clicked.connect(self.selectHandler)
+
+ def setAst(self, ast):
+ """ Create a new model and add all ast elements to it """
+ model = QStandardItemModel()
+ if ast:
+ astToNamedElement(ast, model.invisibleRootItem())
+ self.setModel( model )
+ self.expandAll()
+
+ def selectHandler(self, index):
+ if not index.isValid():
+ return
+ model = self.model()
+ item = model.itemFromIndex(index)
+ node = item.data()
+ self.sigNodeSelected.emit(node)
+
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/ide/codeeditor.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/ide/codeeditor.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,140 @@
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+import compiler.lexer
+import os.path
+
+class MySyntaxHighlighter(QSyntaxHighlighter):
+ def __init__(self, parent=None):
+ super(MySyntaxHighlighter, self).__init__(parent)
+ # Syntax highlighting:
+ self.rules = []
+ fmt = QTextCharFormat()
+ fmt.setForeground(Qt.darkBlue)
+ fmt.setFontWeight(QFont.Bold)
+ for kw in compiler.lexer.keywords:
+ pattern = '\\b'+kw+'\\b'
+ self.rules.append( (pattern, fmt) )
+
+ # Comments:
+ fmt = QTextCharFormat()
+ fmt.setForeground(Qt.gray)
+ fmt.setFontItalic(True)
+ pattern = '\{.*\}'
+ self.rules.append( (pattern, fmt) )
+
+ # Procedure:
+ fmt = QTextCharFormat()
+ fmt.setForeground(Qt.blue)
+ fmt.setFontItalic(True)
+ #pattern = '(?<=procedure )[A-Za-z]'
+ # TODO lookbehind does not work, think something else
+ #self.rules.append( (pattern, fmt) )
+
+ def highlightBlock(self, text):
+ for pattern, fmt in self.rules:
+ expression = QRegExp(pattern)
+ index = expression.indexIn(text)
+ while index >= 0:
+ length = expression.matchedLength()
+ self.setFormat(index, length, fmt)
+ index = expression.indexIn(text, index + length)
+
+class LineNumberArea(QWidget):
+ def __init__(self, codeedit):
+ super(LineNumberArea, self).__init__(codeedit)
+ self.codeedit = codeedit
+ # TODO: display error in this: self.setToolTip('hello world')
+ def sizeHint(self):
+ return QSize(self.codeedit.lineNumberAreaWidth(), 0)
+ def paintEvent(self, ev):
+ self.codeedit.lineNumberAreaPaintEvent(ev)
+
+class CodeEdit(QPlainTextEdit):
+ def __init__(self, parent=None):
+ super(CodeEdit, self).__init__(parent)
+ # members:
+ self.isUntitled = True
+ self.filename = None
+ self.setFont(QFont('Courier'))
+ self.lineNumberArea = LineNumberArea(self)
+
+ self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
+ self.updateRequest.connect(self.updateLineNumberArea)
+
+ # Syntax highlighter:
+ self.highlighter = MySyntaxHighlighter(self.document())
+
+ def setFileName(self, filename):
+ self.filename = filename
+ self.isUntitled = False
+ self.setWindowTitle(filename)
+ def setSource(self, source):
+ self.setPlainText(source)
+
+ def save(self):
+ pass
+ def saveAs(self):
+ pass
+
+ def saveFile(self):
+ if self.isUntitled:
+ self.saveAs()
+ else:
+ source = str(self.toPlainText())
+ f = open(self.filename, 'w')
+ f.write(source)
+ f.close()
+
+ def highlightErrorLocation(self, row, col):
+ tc = QTextCursor(self.document())
+ tc.clearSelection()
+ tc.movePosition(tc.Down, tc.MoveAnchor, row - 1)
+ tc.movePosition(tc.Right, tc.MoveAnchor, col - 1)
+ tc.movePosition(tc.NextCharacter, tc.KeepAnchor) # Select 1 character
+ selection = QTextEdit.ExtraSelection()
+ lineColor = QColor(Qt.red).lighter(160)
+ selection.format.setBackground(lineColor)
+ #selection.format.setProperty(QTextFormat.FullWidthSelection, True)
+ selection.cursor = tc
+ self.setExtraSelections( [ selection ] )
+ def clearErrors(self):
+ self.setExtraSelections( [ ] )
+
+ def lineNumberAreaWidth(self):
+ digits = 1
+ mx = max(1, self.blockCount())
+ while mx >= 10:
+ mx = mx / 10
+ digits += 1
+ space = 3 + self.fontMetrics().width('8') * digits
+ return space
+ def lineNumberAreaPaintEvent(self, ev):
+ painter = QPainter(self.lineNumberArea)
+ painter.fillRect(ev.rect(), Qt.lightGray)
+ block = self.firstVisibleBlock()
+ blockNumber = block.blockNumber()
+ top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
+ bottom = top + self.blockBoundingRect(block).height()
+ while block.isValid() and top <= ev.rect().bottom():
+ if block.isVisible() and bottom >= ev.rect().top():
+ num = str(blockNumber + 1)
+ painter.setPen(Qt.black)
+ painter.drawText(0, top, self.lineNumberArea.width(), self.fontMetrics().height(), Qt.AlignRight, num)
+ block = block.next()
+ top = bottom
+ bottom = top + self.blockBoundingRect(block).height()
+ blockNumber += 1
+ def resizeEvent(self, ev):
+ super(CodeEdit, self).resizeEvent(ev)
+ cr = self.contentsRect()
+ self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height() ))
+ def updateLineNumberAreaWidth(self, newBlockCount):
+ self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
+ def updateLineNumberArea(self, rect, dy):
+ if dy > 0:
+ self.lineNumberArea.scroll(0, dy)
+ else:
+ self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height())
+ if rect.contains(self.viewport().rect()):
+ self.updateLineNumberAreaWidth(0)
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/ide/ide.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/ide/ide.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,309 @@
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+# ide components:
+from .codeeditor import CodeEdit
+from .astviewer import AstViewer
+import base64
+from project import Project
+import os.path
+
+lcfospng = base64.decodestring(b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEhMKBk7B678AAAA/SURBVFjD\n7dbBCQAgDATBi9h/y7EFA4Kf2QLCwH1S6XQu6sqoujublc8BAAAAAAAAAAB8B+zXT6YJAAAAAKYd\nWSgFQNUyijIAAAAASUVORK5CYII=\n')
+
+class BuildOutput(QTextEdit):
+ """ Build output component """
+ def __init__(self, parent=None):
+ super(BuildOutput, self).__init__(parent)
+ self.setCurrentFont(QFont('Courier'))
+ self.setReadOnly(True)
+ self.append('Build output will appear here!')
+
+class BuildErrors(QListView):
+ sigErrorSelected = pyqtSignal(object)
+ def __init__(self, parent=None):
+ super(BuildErrors, self).__init__(parent)
+ model = QStandardItemModel()
+ self.setModel(model)
+ self.clicked.connect(self.itemSelected)
+ def setErrorList(self, errorlist):
+ model = QStandardItemModel()
+ for e in errorlist:
+ row, col, msg = e
+ item = QStandardItem(str(msg))
+ item.setData(e)
+ model.appendRow(item)
+ self.setModel(model)
+ def itemSelected(self, index):
+ if not index.isValid():
+ return
+ model = self.model()
+ item = model.itemFromIndex(index)
+ err = item.data()
+ self.sigErrorSelected.emit(err)
+
+class ProjectView(QWidget):
+ sigLoadFile = pyqtSignal(str)
+ def __init__(self, parent=None):
+ super(ProjectView, self).__init__(parent)
+ self.treeview = QTreeView(self)
+ self.treeview.setContextMenuPolicy(Qt.CustomContextMenu)
+ l = QVBoxLayout(self)
+ l.addWidget(self.treeview)
+ pm = QPixmap()
+ pm.loadFromData(lcfospng)
+ self.projectIcon = QIcon(pm)
+ # Connect signals:
+ self.treeview.activated.connect(self.activate)
+ self.treeview.customContextMenuRequested.connect(self.contextMenu)
+ def setProject(self, project):
+ self.project = project
+ model = QStandardItemModel()
+ root = model.invisibleRootItem()
+ pitem = QStandardItem(self.projectIcon, project.name)
+ pitem.setEditable(False)
+ pitem.setData(project)
+ root.appendRow(pitem)
+ for f in self.project.files:
+ fitem = QStandardItem(f)
+ pitem.appendRow(fitem)
+ fitem.setEditable(False)
+ fitem.setData(f)
+ self.treeview.setModel(model)
+ self.treeview.expandAll()
+ def contextMenu(self, pos):
+ idx = self.treeview.indexAt(pos)
+ if not idx.isValid():
+ return
+ item = self.treeview.model().itemFromIndex(idx)
+ def activate(self, index):
+ if not index.isValid():
+ return
+ model = self.treeview.model()
+ item = model.itemFromIndex(index)
+ fn = item.data()
+ if type(fn) is str:
+ self.sigLoadFile.emit(fn)
+
+class AboutDialog(QDialog):
+ def __init__(self, parent=None):
+ super(AboutDialog, self).__init__(parent)
+ self.setWindowTitle('About')
+ l = QVBoxLayout(self)
+ txt = QTextEdit(self)
+ txt.setReadOnly(True)
+ aboutText = """lcfOS IDE
+ An all-in-one IDE for OS development.
+ https://www.assembla.com/spaces/lcfOS/wiki
+ Author: Windel Bouwman
+ """
+ txt.append(aboutText)
+ l.addWidget(txt)
+ but = QPushButton('OK')
+ but.setDefault(True)
+ but.clicked.connect(self.close)
+ l.addWidget(but)
+
+class ProjectOptions(QDialog):
+ pass
+ # TODO: project options in here
+
+class Ide(QMainWindow):
+ def __init__(self, parent=None):
+ super(Ide, self).__init__(parent)
+ self.setWindowTitle('LCFOS IDE')
+ icon = QPixmap()
+ icon.loadFromData(lcfospng)
+ self.setWindowIcon(QIcon(icon))
+
+ # Create menus:
+ self.fileMenu = self.menuBar().addMenu('File')
+ self.viewMenu = self.menuBar().addMenu('View')
+ self.projectMenu = self.menuBar().addMenu('Project')
+ self.helpMenu = self.menuBar().addMenu('Help')
+
+ # Create mdi area:
+ self.mdiArea = QMdiArea()
+ self.setCentralWidget(self.mdiArea)
+
+ # Create components:
+ self.buildOutput = BuildOutput()
+ self.addComponent('Build output', self.buildOutput)
+
+ self.astViewer = AstViewer()
+ self.addComponent('AST viewer', self.astViewer)
+ self.astViewer.sigNodeSelected.connect(self.nodeSelected)
+
+ self.builderrors = BuildErrors()
+ self.addComponent('Build errors', self.builderrors)
+ self.builderrors.sigErrorSelected.connect(self.errorSelected)
+
+ self.projectview = ProjectView()
+ self.addComponent('Project', self.projectview)
+ self.projectview.sigLoadFile.connect(self.loadFile)
+
+ # About dialog:
+ self.aboutDialog = AboutDialog()
+ self.aboutDialog.setWindowIcon(QIcon(icon))
+ # Create actions:
+ self.buildAction = QAction('Build!', self)
+ self.buildAction.setShortcut(QKeySequence('F7'))
+ self.projectMenu.addAction(self.buildAction)
+ self.buildAction.triggered.connect(self.buildFile)
+ self.openProjectAction = QAction("Open project", self)
+ self.openProjectAction.triggered.connect(self.openProject)
+ self.projectMenu.addAction(self.openProjectAction)
+ self.helpAction = QAction('Help', self)
+ self.helpAction.setShortcut(QKeySequence('F1'))
+ self.helpMenu.addAction(self.helpAction)
+ self.aboutAction = QAction('About', self)
+ self.helpMenu.addAction(self.aboutAction)
+ self.aboutAction.triggered.connect(self.aboutDialog.open)
+
+ self.newFileAction = QAction("New File", self)
+ self.fileMenu.addAction(self.newFileAction)
+ self.newFileAction.triggered.connect(self.newFile)
+ self.saveFileAction = QAction("Save File", self)
+ self.fileMenu.addAction(self.saveFileAction)
+ self.saveFileAction.triggered.connect(self.saveFile)
+ self.closeFileAction = QAction("Close File", self)
+ self.fileMenu.addAction(self.closeFileAction)
+ self.closeFileAction.triggered.connect(self.closeFile)
+
+ cascadeAction = QAction("Cascade windows", self)
+ cascadeAction.triggered.connect(self.mdiArea.cascadeSubWindows)
+ self.viewMenu.addAction(cascadeAction)
+ tileAction = QAction('Tile windows', self)
+ tileAction.triggered.connect(self.mdiArea.tileSubWindows)
+ self.viewMenu.addAction(tileAction)
+
+ # Load settings:
+ self.settings = QSettings('windelsoft', 'lcfoside')
+ self.loadSettings()
+
+ def addComponent(self, name, widget):
+ dw = QDockWidget(name)
+ dw.setWidget(widget)
+ dw.setObjectName(name)
+ self.addDockWidget(Qt.RightDockWidgetArea, dw)
+ self.viewMenu.addAction(dw.toggleViewAction())
+
+ # File handling:
+ def newFile(self):
+ ce = CodeEdit()
+ w = self.mdiArea.addSubWindow(ce)
+ ce.show()
+
+ def saveFile(self):
+ ac = self.activeMdiChild()
+ if ac:
+ ac.saveFile()
+
+ def saveAll(self):
+ pass
+
+ def openFile(self):
+ # TODO
+ pass
+
+ def closeFile(self):
+ ac = self.activeMdiChild()
+ if ac:
+ self.mdiArea.removeSubWindow(ac)
+
+ def loadFile(self, filename):
+ # Find existing mdi widget:
+ wid = self.findMdiChild(filename)
+ if wid:
+ self.mdiArea.setActiveSubWindow(wid.parent())
+ return wid
+
+ # Create a new one:
+ ce = CodeEdit()
+ source = self.project.loadProjectFile(filename)
+ ce.setSource(source)
+ self.mdiArea.addSubWindow(ce)
+ ce.show()
+ return ce
+
+ # MDI:
+ def activeMdiChild(self):
+ aw = self.mdiArea.activeSubWindow()
+ if aw:
+ return aw.widget()
+ else:
+ return None
+
+ def findMdiChild(self, filename):
+ for window in self.mdiArea.subWindowList():
+ wid = window.widget()
+ if wid.filename == filename:
+ return wid
+ return None
+
+ def allChildren(self):
+ c = []
+ for window in self.mdiArea.subWindowList():
+ wid = window.widget()
+ c.append(wid)
+ return c
+
+ # Settings:
+ def loadSettings(self):
+ if self.settings.contains('mainwindowstate'):
+ self.restoreState(self.settings.value('mainwindowstate'))
+ if self.settings.contains('mainwindowgeometry'):
+ self.restoreGeometry(self.settings.value('mainwindowgeometry'))
+ if self.settings.contains('openedproject'):
+ projectfile = self.settings.value('openedproject')
+ self.loadProject(projectfile)
+
+ def closeEvent(self, ev):
+ self.settings.setValue('mainwindowstate', self.saveState())
+ self.settings.setValue('mainwindowgeometry', self.saveGeometry())
+ if self.project:
+ self.settings.setValue('openedproject', self.project.filename)
+ # TODO: ask for save of opened files
+ ev.accept()
+
+ # Error handling:
+ def nodeSelected(self, node):
+ ce = self.activeMdiChild()
+ if not ce:
+ return
+ if node.location:
+ row, col = node.location
+ ce.highlightErrorLocation( row, col )
+ else:
+ ce.clearErrors()
+
+ def errorSelected(self, err):
+ row, col, msg = err
+ ce = self.activeMdiChild()
+ if not ce:
+ return
+ ce.highlightErrorLocation(row, col)
+
+ # Project loading:
+ def loadProject(self, filename):
+ self.project = Project(filename)
+ self.projectview.setProject(self.project)
+
+ def openProject(self):
+ filename = QFileDialog.getOpenFileName(self, \
+ "Choose project file", "", "lcfos Project files (*.lcp)")
+ if filename:
+ self.loadProject(filename)
+
+ # Build recepy:
+ def buildFile(self):
+ """ Build project """
+ self.saveAll()
+ self.buildOutput.clear()
+ self.buildOutput.append(str(self.compiler))
+ mods = self.compiler.compileProject(self.project)
+
+ self.builderrors.setErrorList(self.compiler.errorlist)
+ self.astViewer.setAst(mods[0])
+ for err in self.compiler.errorlist:
+ self.buildOutput.append(str(err))
+ self.buildOutput.append("Done!")
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/project.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/project.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,70 @@
+"""
+ Project that can be stored to and loaded from XML.
+"""
+
+from xml.sax import ContentHandler, make_parser
+import xml.dom.minidom as md
+import os.path
+
+class ProjectContentHandler(ContentHandler):
+ def __init__(self, project):
+ self.project = project
+ self.inFiles = False
+ def startElement(self, name, attrs):
+ if name == 'Project':
+ self.project.name = attrs['name']
+ if name == 'Files':
+ self.inFiles = True
+ if name == 'File' and self.inFiles:
+ self.project.files.append(attrs['Filename'])
+ def endElement(self, name):
+ if name == 'Files':
+ self.inFiles = False
+
+class ProjectFile:
+ def __init__(self, filename):
+ self.filename = filename
+
+class Project:
+ def __init__(self, filename, isnew=False):
+ self.name = ""
+ self.files = []
+ self.settings = {}
+ self.filename = filename
+
+ if not isnew:
+ """ Load the project from the XML file """
+ parser = make_parser()
+ handler = ProjectContentHandler(self)
+ parser.setContentHandler(handler)
+ parser.parse(filename)
+
+ def save(self, filename):
+ """ Save the project in XML format """
+ # Create document:
+ doc = md.Document()
+ # Add project:
+ project = doc.createElement("Project")
+ project.setAttribute("name", self.name)
+ doc.appendChild(project)
+
+ # Add project files:
+ filesNode = doc.createElement("Files")
+ project.appendChild(filesNode)
+ for f in self.files:
+ fe = doc.createElement("File")
+ fe.setAttribute("Filename", f)
+ filesNode.appendChild(fe)
+
+ # Write the XML file:
+ xml = doc.toprettyxml()
+ with open(filename, 'w') as f:
+ f.write(xml)
+
+ def loadProjectFile(self, filename):
+ basedir = os.path.dirname(self.filename)
+ filename = os.path.join(basedir, filename)
+ with open(filename, 'r') as f:
+ src = f.read()
+ return src
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/runbuild.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/runbuild.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,16 @@
+import sys
+
+# Compiler imports:
+from compiler.compiler import Compiler
+from project import Project
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print('Use {0} projectfile'.format(sys.argv[0]))
+ sys.exit(-1)
+ filename = sys.argv[1]
+ project = Project()
+ project.load(filename)
+ pc = Compiler()
+ pc.compileProject(project)
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/runide.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/runide.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,14 @@
+import sys
+from PyQt4.QtGui import QApplication
+
+# Compiler imports:
+from compiler.compiler import Compiler
+from ide.ide import Ide
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ ide = Ide()
+ ide.compiler = Compiler()
+ ide.show()
+ app.exec_()
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/runtests.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/runtests.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,272 @@
+import unittest
+import os
+
+from compiler.compiler import Compiler
+from compiler.errors import CompilerException, printError
+from compiler import lexer
+from compiler.parser import Parser
+from compiler import assembler
+from compiler.codegenerator import CodeGenerator
+from project import Project
+
+class CompilerTestCase(unittest.TestCase):
+ """ test methods start with 'test*' """
+ def testSource1(self):
+ source = """
+ module lcfos;
+ var
+ a : integer;
+
+ procedure putchar(num : integer);
+ begin
+ end putchar;
+
+ procedure WriteNum( num: integer);
+ var
+ d, base : integer;
+ dgt : integer;
+ begin
+ d := 1;
+ base := 10;
+ while num div d >= base do
+ d := d * base
+ end;
+ while d <> 0 do
+ dgt := num div d;
+ num := num mod d;
+ d := d div base;
+ putchar(48 + dgt)
+ end
+ end WriteNum;
+
+ begin
+ a := 1;
+ while a < 26
+ do
+ putchar(65+a);
+ a := a * 2
+ end;
+ end lcfos.
+ """
+ pc = Compiler()
+ pc.compilesource(source)
+ def testSource2(self):
+ source = """
+ module lcfos;
+ var
+ a, b : integer;
+ arr: array 30 of integer;
+ arr2: array 10, 12 of integer;
+ procedure t2*() : integer;
+ begin
+ a := 2;
+ while a < 5 do
+ b := arr[a-1] + arr[a-2];
+ arr2[a,2] := b;
+ arr2[a,3] := arr2[a,2] + arr2[a,2]*3 + b;
+ arr[a] := b;
+ a := a + 1;
+ end;
+ return b
+ end t2;
+ begin
+ b := 12;
+ arr[0] := 1;
+ arr[1] := 1;
+ end lcfos.
+ """
+ pc = Compiler()
+ mod = pc.compilesource(source)
+ def testSource5(self):
+ source = """
+ module lcfos;
+ procedure WriteLn() : integer;
+ const zzz = 13;
+ var
+ a, b, c: integer;
+ begin
+ a := 2;
+ b := 7;
+ c := 10 * a + b*10*a;
+ return c
+ end WriteLn;
+ begin end lcfos.
+ """
+ pc = Compiler()
+ pc.compilesource(source)
+ def tstForStatement(self):
+ source = """
+ module fortest;
+ var
+ a,b,c : integer;
+ begin
+ c := 0;
+ for a := 1 to 10 by 1 do
+ b := a + 15;
+ c := c + b * a;
+ end;
+ end fortest.
+ """
+ pc = Compiler()
+ pc.compilesource(source)
+ def testSourceIfAndWhilePattern(self):
+ source = """
+ module lcfos;
+ procedure WriteLn() : integer;
+ const zzz = 13;
+ var
+ a, b, c: integer;
+ begin
+ a := 1;
+ b := 2;
+ if a * 3 > b then
+ c := 10*a + b*10*a*a*a*b;
+ else
+ c := 13;
+ end;
+ while a < 101 do
+ a := a + 1;
+ c := c + 2;
+ end;
+ return c
+ end WriteLn;
+ begin end lcfos.
+ """
+ pc = Compiler()
+ pc.compilesource(source)
+
+ def testPattern1(self):
+ """ Test if expression can be compiled into byte code """
+ src = "12*13+33-12*2*3"
+ tokens = lexer.tokenize(src)
+ ast = Parser(tokens).parseExpression()
+ code = CodeGenerator().genexprcode(ast)
+
+ def testAssembler(self):
+ """ Check all kind of assembler cases """
+ assert(assembler.shortjump(5) == [0xeb, 0x5])
+ assert(assembler.shortjump(-2) == [0xeb, 0xfc])
+ assert(assembler.shortjump(10,'GE') == [0x7d, 0xa])
+ assert(assembler.nearjump(5) == [0xe9, 0x5,0x0,0x0,0x0])
+ assert(assembler.nearjump(-2) == [0xe9, 0xf9, 0xff,0xff,0xff])
+ assert(assembler.nearjump(10,'LE') == [0x0f, 0x8e, 0xa,0x0,0x0,0x0])
+
+ def testCall(self):
+ assert(assembler.call('r10') == [0x41, 0xff, 0xd2])
+ assert(assembler.call('rcx') == [0xff, 0xd1])
+ def testXOR(self):
+ assert(assembler.xorreg64('rax', 'rax') == [0x48, 0x31, 0xc0])
+ assert(assembler.xorreg64('r9', 'r8') == [0x4d, 0x31, 0xc1])
+ assert(assembler.xorreg64('rbx', 'r11') == [0x4c, 0x31, 0xdb])
+
+ def testINC(self):
+ assert(assembler.increg64('r11') == [0x49, 0xff, 0xc3])
+ assert(assembler.increg64('rcx') == [0x48, 0xff, 0xc1])
+
+ def testPush(self):
+ assert(assembler.push('rbp') == [0x55])
+ assert(assembler.push('rbx') == [0x53])
+ assert(assembler.push('r12') == [0x41, 0x54])
+ def testPop(self):
+ assert(assembler.pop('rbx') == [0x5b])
+ assert(assembler.pop('rbp') == [0x5d])
+ assert(assembler.pop('r12') == [0x41, 0x5c])
+
+ def testAsmLoads(self):
+ # TODO constant add testcases
+ assert(assembler.mov('rbx', 'r14') == [0x4c, 0x89, 0xf3])
+ assert(assembler.mov('r12', 'r8') == [0x4d, 0x89, 0xc4])
+ assert(assembler.mov('rdi', 'rsp') == [0x48, 0x89, 0xe7])
+
+ def testAsmMemLoads(self):
+ assert(assembler.mov('rax', ['r8','r15',0x11]) == [0x4b,0x8b,0x44,0x38,0x11])
+ assert(assembler.mov('r13', ['rbp','rcx',0x23]) == [0x4c,0x8b,0x6c,0xd,0x23])
+
+ assert(assembler.mov('r9', ['rbp',-0x33]) == [0x4c,0x8b,0x4d,0xcd])
+ #assert(assembler.movreg64('rbx', ['rax']) == [0x48, 0x8b,0x18])
+
+ assert(assembler.mov('rax', [0xb000]) == [0x48,0x8b,0x4,0x25,0x0,0xb0,0x0,0x0])
+ assert(assembler.mov('r11', [0xa0]) == [0x4c,0x8b,0x1c,0x25,0xa0,0x0,0x0,0x0])
+
+ assert(assembler.mov('r11', ['RIP', 0xf]) == [0x4c,0x8b,0x1d,0x0f,0x0,0x0,0x0])
+
+ def testAsmMemStores(self):
+ assert(assembler.mov(['rbp', 0x13],'rbx') == [0x48,0x89,0x5d,0x13])
+ assert(assembler.mov(['r12', 0x12],'r9') == [0x4d,0x89,0x4c,0x24,0x12])
+ assert(assembler.mov(['rcx', 0x11],'r14') == [0x4c,0x89,0x71,0x11])
+
+
+ assert(assembler.mov([0xab], 'rbx') == [0x48,0x89,0x1c,0x25,0xab,0x0,0x0,0x0])
+ assert(assembler.mov([0xcd], 'r13') == [0x4c,0x89,0x2c,0x25,0xcd,0x0,0x0,0x0])
+
+ assert(assembler.mov(['RIP', 0xf], 'r9') == [0x4c,0x89,0x0d,0x0f,0x0,0x0,0x0])
+
+ def testAsmMOV8(self):
+ assert(assembler.mov(['rbp', -8], 'al') == [0x88, 0x45, 0xf8])
+ assert(assembler.mov(['r11', 9], 'cl') == [0x41, 0x88, 0x4b, 0x09])
+
+ assert(assembler.mov(['rbx'], 'al') == [0x88, 0x03])
+ assert(assembler.mov(['r11'], 'dl') == [0x41, 0x88, 0x13])
+
+ def testAsmLea(self):
+ assert(assembler.leareg64('r11', ['RIP', 0xf]) == [0x4c,0x8d,0x1d,0x0f,0x0,0x0,0x0])
+ assert(assembler.leareg64('rsi', ['RIP', 0x7]) == [0x48,0x8d,0x35,0x07,0x0,0x0,0x0])
+
+ assert(assembler.leareg64('rcx', ['rbp', -8]) == [0x48,0x8d,0x4d,0xf8])
+
+ def testAssemblerCMP(self):
+ assert(assembler.cmpreg64('rdi', 'r13') == [0x4c, 0x39, 0xef])
+ assert(assembler.cmpreg64('rbx', 'r14') == [0x4c, 0x39, 0xf3])
+ assert(assembler.cmpreg64('r12', 'r9') == [0x4d, 0x39, 0xcc])
+
+ assert(assembler.cmpreg64('rdi', 1) == [0x48, 0x83, 0xff, 0x01])
+ assert(assembler.cmpreg64('r11', 2) == [0x49, 0x83, 0xfb, 0x02])
+ def testAssemblerADD(self):
+ assert(assembler.addreg64('rbx', 'r13') == [0x4c, 0x01, 0xeb])
+ assert(assembler.addreg64('rax', 'rbx') == [0x48, 0x01, 0xd8])
+ assert(assembler.addreg64('r12', 'r13') == [0x4d, 0x01, 0xec])
+
+ assert(assembler.addreg64('rbx', 0x13) == [0x48, 0x83, 0xc3, 0x13])
+ assert(assembler.addreg64('r11', 0x1234567) == [0x49, 0x81, 0xc3, 0x67, 0x45,0x23,0x1])
+ assert(assembler.addreg64('rsp', 0x33) == [0x48, 0x83, 0xc4, 0x33])
+
+ def testAssemblerSUB(self):
+ assert(assembler.subreg64('rdx', 'r14') == [0x4c, 0x29, 0xf2])
+ assert(assembler.subreg64('r15', 'rbx') == [0x49, 0x29, 0xdf])
+ assert(assembler.subreg64('r8', 'r9') == [0x4d, 0x29, 0xc8])
+
+ assert(assembler.subreg64('rsp', 0x123456) == [0x48, 0x81, 0xec, 0x56,0x34,0x12,0x0])
+ assert(assembler.subreg64('rsp', 0x12) == [0x48, 0x83, 0xec, 0x12])
+
+ def testAssemblerIDIV(self):
+ assert(assembler.idivreg64('r11') == [0x49, 0xf7, 0xfb])
+ assert(assembler.idivreg64('rcx') == [0x48, 0xf7, 0xf9])
+ assert(assembler.idivreg64('rsp') == [0x48, 0xf7, 0xfc])
+
+ def testAssemblerIMUL(self):
+ assert(assembler.imulreg64_rax('rdi') == [0x48, 0xf7, 0xef])
+ assert(assembler.imulreg64_rax('r10') == [0x49, 0xf7, 0xea])
+ assert(assembler.imulreg64_rax('rdx') == [0x48, 0xf7, 0xea])
+
+ assert(assembler.imulreg64('r11', 'rdi') == [0x4c, 0xf, 0xaf, 0xdf])
+ assert(assembler.imulreg64('r12', 'rbx') == [0x4c, 0xf, 0xaf, 0xe3])
+ # nasm generates this machine code: 0x4d, 0x6b, 0xff, 0xee
+ # This also works: 4D0FAFFE (another variant?? )
+ assert(assembler.imulreg64('r15', 'r14') == [0x4d, 0x0f, 0xaf, 0xfe])
+ def testProject(self):
+ p = Project('test.xml', isnew=True)
+ p.name = "Test project"
+ p.files.append('main.mod')
+ p.files.append('test.mod')
+ p.save('test.xml')
+
+ q = Project('test.xml')
+
+ assert(p.name == q.name)
+ assert(p.files == q.files)
+ # TODO: remove test.xml test file
+ os.remove('test.xml')
+
+if __name__ == '__main__':
+ unittest.main()
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/testproject/main.mod
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/testproject/main.mod Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,25 @@
+module main;
+var
+ a,b,c : integer;
+
+procedure test(x:integer);
+ var y,z:integer;
+begin
+ y := x * 3 + 2;
+ z := x + y + a;
+end test;
+
+procedure add(a:integer; b:integer):integer;
+ var
+ tmp : integer;
+ begin
+ tmp := a + b;
+ return tmp
+ end add;
+
+begin
+ a := 12;
+ b := a * 12 + 33;
+ c := a div b + a * b * 99;
+end main.
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/testproject/test.lcp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/testproject/test.lcp Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/ide/testproject/test.mod
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ide/testproject/test.mod Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,25 @@
+module test;
+var
+ a,b,c : integer;
+
+procedure test(x:integer);
+ var y,z:integer;
+begin
+ y := x * 3 + 2;
+ z := x + y + a;
+end test;
+
+procedure add(a:integer; b:integer):integer;
+ var
+ tmp : integer;
+ begin
+ tmp := a + b;
+ return tmp
+ end add;
+
+begin
+ a := 12;
+ b := a * 12 + 33;
+ c := a div b + a * b * 99;
+end test.
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/lab/bouncing_cube.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/lab/bouncing_cube.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,406 @@
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+from PyQt4.QtOpenGL import QGLWidget
+from OpenGL.GL import *
+from OpenGL.GLU import gluPerspective
+import sys
+from random import random
+from math import pi, cos, sin, fabs, sqrt
+from numpy import mat, array, ones, zeros, eye
+from numpy.linalg import norm
+import numpy as np
+import time
+import scipy.integrate
+#import pyopencl
+
+"""
+ Test script that lets a dice bounce.
+ Converted from 20-sim equations into python code.
+
+ 20-sim website:
+ http://www.20sim.com
+
+"""
+def drawCube(w):
+ glBegin(GL_QUADS) # Start Drawing The Cube
+ glColor3f(0.0,1.0,0.0) # Set The Color To Blue
+ glVertex3f( w, w,-w) # Top Right Of The Quad (Top)
+ glVertex3f(-w, w,-w) # Top Left Of The Quad (Top)
+ glVertex3f(-w, w, w) # Bottom Left Of The Quad (Top)
+ glVertex3f( w, w, w) # Bottom Right Of The Quad (Top)
+
+ glColor3f(1.0,0.5,0.0) # Set The Color To Orange
+ glVertex3f( w,-w, w) # Top Right Of The Quad (Bottom)
+ glVertex3f(-w,-w, w) # Top Left Of The Quad (Bottom)
+ glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Bottom)
+ glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Bottom)
+
+ glColor3f(1.0,0.0,0.0) # Set The Color To Red
+ glVertex3f( w, w, w) # Top Right Of The Quad (Front)
+ glVertex3f(-w, w, w) # Top Left Of The Quad (Front)
+ glVertex3f(-w,-w, w) # Bottom Left Of The Quad (Front)
+ glVertex3f( w,-w, w) # Bottom Right Of The Quad (Front)
+
+ glColor3f(1.0,1.0,0.0) # Set The Color To Yellow
+ glVertex3f( w,-w,-w) # Bottom Left Of The Quad (Back)
+ glVertex3f(-w,-w,-w) # Bottom Right Of The Quad (Back)
+ glVertex3f(-w, w,-w) # Top Right Of The Quad (Back)
+ glVertex3f( w, w,-w) # Top Left Of The Quad (Back)
+
+ glColor3f(0.0,0.0,1.0) # Set The Color To Blue
+ glVertex3f(-w, w, w) # Top Right Of The Quad (Left)
+ glVertex3f(-w, w,-w) # Top Left Of The Quad (Left)
+ glVertex3f(-w,-w,-w) # Bottom Left Of The Quad (Left)
+ glVertex3f(-w,-w, w) # Bottom Right Of The Quad (Left)
+
+ glColor3f(1.0,0.0,1.0) # Set The Color To Violet
+ glVertex3f( w, w,-w) # Top Right Of The Quad (Right)
+ glVertex3f( w, w, w) # Top Left Of The Quad (Right)
+ glVertex3f( w,-w, w) # Bottom Left Of The Quad (Right)
+ glVertex3f( w,-w,-w) # Bottom Right Of The Quad (Right)
+ glEnd() # Done Drawing The Quad
+
+def drawFloor(w, h):
+ glBegin(GL_QUADS) # Start Drawing The Cube
+
+ glColor3f(1.0,0.5,0.0) # Set The Color To Orange
+ glVertex3f( w,-w,h)# Top Right Of The Quad (Bottom)
+ glVertex3f(-w,-w,h)# Top Left Of The Quad (Bottom)
+ glVertex3f(-w,w,h)# Bottom Left Of The Quad (Bottom)
+ glVertex3f( w,w,h)# Bottom Right Of The Quad (Bottom)
+ glEnd() # Done Drawing The Quad
+
+def drawAxis():
+ glLineWidth(0.5)
+ glBegin(GL_LINES)
+ glColor3f(1.0, 0.0, 0.0)
+ glVertex3f(0,0,0)
+ glVertex3f(1,0,0)
+ glColor3f(0.0, 1.0, 0.0)
+ glVertex3f(0,0,0)
+ glVertex3f(0,1,0)
+ glColor3f(0.0, 0.0, 1.0)
+ glVertex3f(0,0,0)
+ glVertex3f(0,0,1)
+ glEnd()
+
+
+def cross(A, B):
+ a = A.A1
+ b = B.A1
+ return mat(np.cross(a, b)).T
+
+def skew(X):
+ Y = mat(zeros( (3, 3) ))
+ a,b,c = X.A1
+ Y[0,1] = -c
+ Y[0,2] = b
+ Y[1,0] = c
+ Y[1,2] = -a
+ Y[2,0] = -b
+ Y[2,1] = a
+ return Y
+
+def adjoint(T):
+ W = T[0:3, 0]
+ V = T[3:6, 0]
+ a = mat(zeros( (6,6) ) )
+ a[0:3, 0:3] = skew(W)
+ a[3:6, 0:3] = skew(V)
+ a[3:6, 3:6] = skew(W)
+ return a
+
+def Adjoint(H):
+ R = H[0:3, 0:3]
+ P = H[0:3, 3]
+ a = mat(zeros( (6,6) ) )
+ a[0:3, 0:3] = R
+ a[3:6, 3:6] = R
+ a[3:6, 0:3] = skew(P) * R
+ return a
+
+def quatToR(q):
+ x, y, z, w = q.A1
+ r = mat(eye(3))
+ r[0,0] = 1 - (2*y**2+2*z**2)
+ r[0,1] = 2*x*y+2*z*w
+ r[0,2] = 2*x*z - 2*y*w
+ r[1,0] = 2*x*y-2*z*w
+ r[1,1] = 1 - (2*x**2 + 2*z**2)
+ r[1,2] = 2*y*z + 2*x*w
+ r[2,0] = 2*x*z+2*y*w
+ r[2,1] = 2*y*z - 2*x*w
+ r[2,2] = 1 - (2*x**2+2*y**2)
+ return r
+
+def rotateAbout(axis, angle):
+ ax, ay, az = (axis/norm(axis)).A1
+ qx = ax*sin(angle/2.0)
+ qy = ay*sin(angle/2.0)
+ qz = az*sin(angle/2.0)
+ qw = cos(angle/2.0)
+ q = mat(array([qx,qy,qz,qw])).T
+ return q
+
+def normalizeQuaternion(quat):
+ x,y,z,w = quat.A1
+ magnitude = sqrt(x*x + y*y + z*z + w*w)
+ x = x / magnitude
+ y = y / magnitude
+ z = z / magnitude
+ w = w / magnitude
+ quat[0, 0] = x
+ quat[1, 0] = y
+ quat[2, 0] = z
+ quat[3, 0] = w
+ return quat
+
+def VTo4x4(V):
+ v1, v2, v3 = V.A1
+ return mat(array( \
+ [[0.0, -v3, v2, -v1], \
+ [ v3, 0.0, -v1, -v2], \
+ [-v2, v1, 0.0, -v3], \
+ [v1, v2, v3, 0.0] ]))
+
+def homogeneous(R,p):
+ H = mat(eye(4))
+ H[0:3, 0:3] = R
+ H[0:3, 3] = p
+ return H
+
+def rateOfChange(states, thetime, parameters):
+ quat = states[0:4, 0] # Orientation (4)
+ pos = states[4:7, 0] # Position (3)
+ P = states[7:13, 0] # Momentum (6)
+ massI, gravity = parameters
+ # Rigid body parts:
+ # Forward Kinematic chain:
+ H = homogeneous(quatToR(quat), pos) # Forward kinematics
+
+ AdjX2 = mat(eye(6)) # The connectionpoint in the real world
+ adjAdjX2_1 = adjoint(AdjX2[0:6,0])
+ adjAdjX2_2 = adjoint(AdjX2[0:6,1])
+ adjAdjX2_3 = adjoint(AdjX2[0:6,2])
+ adjAdjX2_4 = adjoint(AdjX2[0:6,3])
+ adjAdjX2_5 = adjoint(AdjX2[0:6,4])
+ adjAdjX2_6 = adjoint(AdjX2[0:6,5])
+ AdjInv2 = Adjoint(H.I)
+ M2 = AdjInv2.T * (massI * AdjInv2) # Transfor mass to base frame
+ MassMatrix = M2
+
+ wrenchGrav2 = mat( zeros((1,6)) )
+ wrenchGrav2[0, 0:3] = -cross(gravity, pos).T
+ wrenchGrav2[0, 3:6] = gravity.T
+
+ Bk = mat( zeros( (6,6) ))
+ Bk[0:3, 0:3] = skew(P[0:3, 0])
+ Bk[3:6, 0:3] = skew(P[3:6, 0])
+ Bk[0:3, 3:6] = skew(P[3:6, 0])
+
+ # TODO: do this a cholesky:
+ v = np.linalg.solve(MassMatrix, P) # Matrix inverse like thingy !
+
+ T2_00 = v # Calculate the relative twist!
+ TM2 = T2_00.T * M2
+ twistExternal = T2_00 # Twist van het blokje
+ TMSum2 = TM2
+
+ PDotBodies = mat( zeros( (6,1)) )
+ PDotBodies[0,0] = TMSum2 * (adjAdjX2_1 * T2_00)
+ PDotBodies[1,0] = TMSum2 * (adjAdjX2_2 * T2_00)
+ PDotBodies[2,0] = TMSum2 * (adjAdjX2_3 * T2_00)
+ PDotBodies[3,0] = TMSum2 * (adjAdjX2_4 * T2_00)
+ PDotBodies[4,0] = TMSum2 * (adjAdjX2_5 * T2_00)
+ PDotBodies[5,0] = TMSum2 * (adjAdjX2_6 * T2_00)
+
+ PDot = -PDotBodies - Bk * v
+ PDot += wrenchGrav2.T
+
+ ##### Contact wrench part:
+ HB_W = H # Is H-matrix van het blokje
+ WrenchB = mat(zeros( (1,6) ))
+ for px in [-0.5, 0.5]:
+ for py in [-0.5, 0.5]:
+ for pz in [-0.5, 0.5]:
+ HB1_B = homogeneous(mat(eye(3)), mat([px,py,pz]).T)
+ HB1_W = HB_W * HB1_B
+ HW1_W = homogeneous(mat(eye(3)), HB1_W[0:3,3])
+ HW_W1 = HW1_W.I
+ HB_W1 = HW_W1 * HB_W
+
+ AdjHB_W1 = Adjoint(HB_W1)
+ TB_W1_W1 = AdjHB_W1 * twistExternal
+ z = HB1_W[2, 3]
+ vx, vy, vz = TB_W1_W1[3:6, 0].A1
+ if z < 0:
+ # Contact forces:
+ Fx = -50.0*vx
+ Fy = -50.0*vy
+ Fz = -z*50000.0
+ else:
+ Fx = 0.0
+ Fy = 0.0
+ Fz = 0.0
+ # TODO: reflect impulse
+ WrenchW1 = mat([0,0,0,0,0,Fz])
+ # Transform it back:
+ WrenchB += (AdjHB_W1.T * WrenchW1.T).T
+ ##### End of contact wrench
+
+ PDot += (WrenchB * AdjInv2).T
+
+ # Position and orientation rates:
+ QOmega = VTo4x4(v[0:3, 0])
+ quatDot = 0.5 * QOmega * quat
+ vel = v[3:6, 0]
+ posDot = skew(v[0:3]) * pos + vel
+ # The rate vector:
+ rates = mat(zeros( (13,1) ))
+ rates[0:4, 0] = quatDot
+ rates[4:7, 0] = posDot
+ rates[7:13, 0] = PDot
+ return rates
+
+def fWrapper(y, t, parameters):
+ y = mat(y).T
+ dy = rateOfChange(y, t, parameters)
+ return dy.T.A1
+
+def SCIPY(endtime, dt, state, parameters):
+ times = np.arange(0.0, endtime, dt)
+ y0 = state.T.A1
+ res = scipy.integrate.odeint(fWrapper, y0, times, args=(parameters,))
+ states = []
+ res = res.T
+ r,c = res.shape
+ for ci in range(0,c):
+ states.append(mat(res[:,ci]).T)
+ return states
+
+def RK4(endtime, dt, state, parameters):
+ t = 0.0
+ states = []
+ while t < endtime:
+ newstate = mat (zeros( state.shape )) # Create a new object
+
+ #### Runge Kutta integration:
+ k1 = rateOfChange(state, t, parameters)
+ k2 = rateOfChange(state + 0.5*dt*k1, t, parameters)
+ k3 = rateOfChange(state + 0.5*dt*k1, t, parameters)
+ k4 = rateOfChange(state + dt*k3, t, parameters)
+ newstate = state + (dt/6.0)*(k1+2*k2+2*k3+k4)
+
+ # Normalize quat:
+ newstate[0:4, 0] = normalizeQuaternion(newstate[0:4, 0])
+ states.append(newstate)
+
+ state = newstate
+
+ t += dt
+ print state[6,0], t, ' ', (t/endtime)*100.0, '%'
+ return states
+
+
+def simulate(endtime, dt):
+ PInitial = mat( zeros((6,1)) )
+ posInitial = mat(array([-1.2, -1.3, 2.8])).T
+ quatInitial = rotateAbout(mat(array([0.2, 1.0, 0.4])).T, 0.5)
+ # Parameters:
+ gravity = mat( array([0,0,-9.81]) ).T
+ massI = mat(eye(6)) * 1.01 # Mass matrix
+ parameters = (massI, gravity)
+
+ # The state vector!
+ state = mat(zeros( (13,1) ))
+ state[0:4, 0] = quatInitial
+ state[4:7, 0] = posInitial
+ state[7:13, 0] = PInitial
+
+ return SCIPY(endtime, dt, state, parameters)
+
+class W(QGLWidget):
+ time = 0.0
+ index = 0
+ def __init__(self, states, dt, parent=None):
+ super(W, self).__init__(parent)
+ self.firstRun = True
+ self.savePNGS = False
+ self.dt = dt
+ self.resize(500,500)
+ self.states = states
+ self.UP()
+ t = QTimer(self)
+ t.timeout.connect(self.UP)
+ t.start(self.dt*1000.0)
+
+ def UP(self):
+ self.time += self.dt
+ if self.states:
+ state = self.states[self.index]
+ self.Dicequat = state[0:4, 0]
+ self.Dicepos = state[4:7, 0]
+ self.index += 1
+ if self.index == len(self.states):
+ self.index = 0
+ self.firstRun = False
+ # Paint:
+ self.update()
+ if self.firstRun:
+ # Create png images for the movie:
+ if self.savePNGS:
+ pm = self.renderPixmap()
+ pm.save('image'+str(self.index)+'.png')
+
+ def initializeGL(self):
+ glClearColor(0.0, 0.5, 0.0, 1.0)
+ glEnable(GL_DEPTH_TEST)
+ glDepthFunc(GL_LESS)
+ glShadeModel(GL_SMOOTH)
+ def resizeGL(self,w,h):
+ glViewport(0, 0, w, h)
+ glMatrixMode(GL_PROJECTION)
+ glLoadIdentity()
+ gluPerspective(45.0, float(w)/float(h), 0.1, 100.0)
+ glMatrixMode(GL_MODELVIEW)
+ def paintGL(self):
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Clear buffers
+ glLoadIdentity() # Reset The View
+
+ glLoadIdentity()
+ glTranslatef(0.0,-2.0,-10.0) # Move Left And Into The Screen
+ glRotatef(-90.0, 1.0, 0.0, 0.0)
+ drawFloor(2.0, 0.0)
+ drawAxis()
+ self.renderText(1.0, 0.0, 0.0, 'X')
+ self.renderText(0.0, 1.0, 0.0, 'Y')
+ self.renderText(0.0, 0.0, 1.0, 'Z')
+
+ self.renderText(0.0,0.0,1.2,str(self.time))
+
+ x,y,z = self.Dicepos.A1
+ R = quatToR(self.Dicequat)
+
+ glTranslatef(x, y, z)
+ # Trick to rotate the openGL matrix:
+ r = R.A1
+ rotR = (r[0], r[3], r[6], 0.0,
+ r[1], r[4], r[7], 0.0,
+ r[2], r[5], r[8], 0.0,
+ 0.0, 0.0, 0.0, 1.0)
+ glMultMatrixd(rotR)
+
+ drawCube(0.6)
+
+et = 20.0
+dt = 0.04
+print 'starting integration... endtime =', et, ' stepsize =', dt
+t0 = time.time()
+states = simulate(et, dt)
+t1 = time.time()
+print 'That was heavy, it took me ', t1-t0, ' seconds!'
+app = QApplication(sys.argv)
+w = W(states, dt)
+w.show()
+sys.exit(app.exec_())
+
diff -r 6fa41208a3a8 -r fd7d5069734e python/lab/diagrameditor.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/lab/diagrameditor.py Sun Oct 07 16:56:50 2012 +0200
@@ -0,0 +1,653 @@
+#!/usr/bin/python
+
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+import sys
+import xml.dom.minidom as md
+import xml
+
+"""
+ Author: Windel Bouwman
+ Year: 2012
+ Description: This script implements a diagram editor.
+ run with python 3.x as:
+ $ python [thisfile.py]
+"""
+
+class ArrowHead(QGraphicsPathItem):
+ def __init__(self, parent):
+ super(ArrowHead, self).__init__(parent)
+ arrowPath = QPainterPath(QPointF(0.0, 0.0))
+ arrowPath.lineTo(-6.0, 10.0)
+ arrowPath.lineTo(6.0, 10.0)
+ arrowPath.lineTo(0.0, 0.0)
+ self.setPath(arrowPath)
+ pen = QPen(Qt.blue, 2)
+ self.setPen(pen)
+ self.setBrush(QBrush(pen.color()))
+ self.myshape = QPainterPath()
+
+class Connection(QGraphicsPathItem):
+ """ Implementation of a connection between blocks """
+ def __init__(self, fromPort, toPort):
+ super(Connection, self).__init__()
+ self.pos1 = None
+ self.pos2 = None
+ self.fromPort = None
+ self.toPort = None
+ self.setFlag(self.ItemIsSelectable, True)
+ self.setFlag(self.ItemClipsToShape, True)
+ self.pen = QPen(Qt.blue, 2)
+ self.pen.setCapStyle(Qt.RoundCap)
+ self.setPen(self.pen)
+ self.arrowhead = ArrowHead(self)
+ self.vias = []
+ self.setFromPort(fromPort)
+ self.setToPort(toPort)
+ def mouseDoubleClickEvent(self, event):
+ pos = event.scenePos()
+ pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2]
+ idx = 0
+ tidx = 0
+ for p1, p2 in zip(pts[0:-1], pts[1:]):
+ l1 = QLineF(p1, p2)
+ l2 = QLineF(p1, pos)
+ l3 = QLineF(pos, p2)
+ d = l2.length() + l3.length() - l1.length()
+ if d < 5:
+ tidx = idx
+ idx += 1
+ self.addHandle(pos, tidx)
+
+ def addHandle(self, pos, idx=None):
+ hi = HandleItem(self)
+ if idx:
+ self.vias.insert(idx, hi)
+ else:
+ self.vias.append(hi)
+ def callback(p):
+ self.updateLineStukken()
+ return p
+ hi.posChangeCallbacks.append(callback)
+ hi.setPos(pos)
+ self.updateLineStukken()
+
+ def setFromPort(self, fromPort):
+ if self.fromPort:
+ self.fromPort.posCallbacks.remove(self.setBeginPos)
+ self.fromPort.connection = None
+ self.fromPort = fromPort
+ if self.fromPort:
+ self.fromPort.connection = self
+ self.setBeginPos(fromPort.scenePos())
+ self.fromPort.posCallbacks.append(self.setBeginPos)
+ def setToPort(self, toPort):
+ if self.toPort:
+ self.toPort.posCallbacks.remove(self.setEndPos)
+ self.toPort.connection = None
+ self.toPort = toPort
+ if self.toPort:
+ self.setEndPos(toPort.scenePos())
+ self.toPort.connection = self
+ self.toPort.posCallbacks.append(self.setEndPos)
+ def releasePorts(self):
+ self.setFromPort(None)
+ self.setToPort(None)
+ def setBeginPos(self, pos1):
+ self.pos1 = pos1
+ self.updateLineStukken()
+ def setEndPos(self, endpos):
+ self.pos2 = endpos
+ self.updateLineStukken()
+ def itemChange(self, change, value):
+ if change == self.ItemSelectedHasChanged:
+ for via in self.vias:
+ via.setVisible(value)
+ return super(Connection, self).itemChange(change, value)
+ def shape(self):
+ return self.myshape
+ def updateLineStukken(self):
+ """
+ This algorithm determines the optimal routing of all signals.
+ TODO: implement nice automatic line router
+ """
+ if self.pos1 is None or self.pos2 is None:
+ return
+ pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2]
+ self.arrowhead.setPos(self.pos2)
+ if pts[-1].x() < pts[-2].x():
+ self.arrowhead.setRotation(-90)
+ else:
+ self.arrowhead.setRotation(90)
+ path = QPainterPath(pts[0])
+ for pt in pts[1:]:
+ path.lineTo(pt)
+ self.setPath(path)
+ """ Create a shape outline using the path stroker """
+ s = super(Connection, self).shape()
+ pps = QPainterPathStroker()
+ pps.setWidth(10)
+ self.myshape = pps.createStroke(s).simplified()
+
+class ParameterDialog(QDialog):
+ def __init__(self, block, parent = None):
+ super(ParameterDialog, self).__init__(parent)
+ self.block = block
+ self.button = QPushButton('Ok', self)
+ l = QGridLayout(self)
+ l.addWidget(QLabel('Name:', self), 0, 0)
+ self.nameEdit = QLineEdit(self.block.name)
+ l.addWidget(self.nameEdit, 0, 1)
+ l.addWidget(QLabel('Code:', self), 1, 0)
+ self.codeEdit = QTextEdit(self)
+ self.codeEdit.setPlainText(self.block.code)
+ l.addWidget(self.codeEdit, 1, 1)
+ l.addWidget(self.button, 2, 0, 1, 2)
+ self.button.clicked.connect(self.OK)
+ def OK(self):
+ self.block.setName(self.nameEdit.text())
+ self.block.code = self.codeEdit.toPlainText()
+ self.close()
+
+class PortItem(QGraphicsPathItem):
+ """ Represents a port to a subsystem """
+ def __init__(self, name, block, direction):
+ super(PortItem, self).__init__(block)
+ self.connection = None
+ path = QPainterPath()
+ d = 10.0
+ if direction == 'input':
+ path.moveTo(-d, -d)
+ path.lineTo(0.0, 0.0)
+ path.lineTo(-d, d)
+ else:
+ path.moveTo(0.0, -d)
+ path.lineTo(d, 0.0)
+ path.lineTo(0.0, d)
+ self.setPath(path)
+ self.direction = direction
+ self.block = block
+ self.setCursor(QCursor(Qt.CrossCursor))
+ pen = QPen(Qt.blue, 2)
+ pen.setCapStyle(Qt.RoundCap)
+ self.setPen(pen)
+ self.name = name
+ self.textItem = QGraphicsTextItem(name, self)
+ self.setName(name)
+ self.posCallbacks = []
+ self.setFlag(self.ItemSendsScenePositionChanges, True)
+ def setName(self, name):
+ self.name = name
+ self.textItem.setPlainText(name)
+ rect = self.textItem.boundingRect()
+ lw, lh = rect.width(), rect.height()
+ if self.direction == 'input':
+ lx = 3
+ else:
+ lx = -3 - lw
+ self.textItem.setPos(lx, -lh / 2)
+ def itemChange(self, change, value):
+ if change == self.ItemScenePositionHasChanged:
+ for cb in self.posCallbacks:
+ cb(value)
+ return value
+ return super(PortItem, self).itemChange(change, value)
+ def mousePressEvent(self, event):
+ if self.direction == 'output':
+ self.scene().startConnection(self)
+
+class OutputPort(PortItem):
+ # TODO: create a subclass OR make a member porttype
+ pass
+
+# Block part:
+class HandleItem(QGraphicsEllipseItem):
+ """ A handle that can be moved by the mouse """
+ def __init__(self, parent=None):
+ dx = 13.0
+ super(HandleItem, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent)
+ self.posChangeCallbacks = []
+ self.setBrush(QBrush(Qt.white))
+ self.setFlag(self.ItemSendsScenePositionChanges, True)
+ self.setFlag(self.ItemIsMovable, True)
+ self.setVisible(False)
+ self.setCursor(QCursor(Qt.SizeFDiagCursor))
+ def mouseMoveEvent(self, event):
+ """ Move function without moving the other selected elements """
+ p = self.mapToParent(event.pos())
+ self.setPos(p)
+ def mySetPos(self, p):
+ # TODO: use this instead of itemChange?
+ self.setPos(p)
+ def itemChange(self, change, value):
+ if change == self.ItemPositionChange:
+ for cb in self.posChangeCallbacks:
+ res = cb(value)
+ if res:
+ value = res
+ return value
+ # Call superclass method:
+ return super(HandleItem, self).itemChange(change, value)
+
+def uniqify(name, names):
+ newname = name
+ i = 1
+ while newname in names:
+ newname = name + str(i)
+ i += 1
+ return newname
+
+class BlockItem(QGraphicsRectItem):
+ """
+ Represents a block in the diagram
+ Has an x and y and width and height
+ width and height can only be adjusted with a tip in the lower right corner.
+
+ - in and output ports
+ - parameters
+ - description
+ """
+ def __init__(self, name='Untitled', parent=None):
+ super(BlockItem, self).__init__(parent)
+ # Properties of the rectangle:
+ self.setPen(QPen(Qt.blue, 2))
+ self.setBrush(QBrush(Qt.lightGray))
+ self.setFlags(self.ItemIsSelectable | self.ItemIsMovable)
+ self.setFlag(self.ItemSendsScenePositionChanges, True)
+ self.setCursor(QCursor(Qt.PointingHandCursor))
+ self.label = QGraphicsTextItem(name, self)
+ self.name = name
+ self.code = ''
+ # Create corner for resize:
+ self.sizer = HandleItem(self)
+ self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback
+ button = QPushButton('+in')
+ button.clicked.connect(self.newInputPort)
+ self.buttonItemAddInput = QGraphicsProxyWidget(self)
+ self.buttonItemAddInput.setWidget(button)
+ self.buttonItemAddInput.setVisible(False)
+ button = QPushButton('+out')
+ button.clicked.connect(self.newOutputPort)
+ self.buttonItemAddOutput = QGraphicsProxyWidget(self)
+ self.buttonItemAddOutput.setWidget(button)
+ self.buttonItemAddOutput.setVisible(False)
+
+ # Inputs and outputs of the block:
+ self.inputs = []
+ self.outputs = []
+ # Update size:
+ self.sizer.mySetPos(QPointF(60, 40)) # This is a better resize function
+ def editParameters(self):
+ pd = ParameterDialog(self, self.window())
+ pd.exec_()
+ def mouseDoubleClickEvent(self, event):
+ self.editParameters()
+ def newInputPort(self):
+ names = [i.name for i in self.inputs + self.outputs]
+ self.addInput(PortItem(uniqify('in', names), self, 'input'))
+ def newOutputPort(self):
+ names = [i.name for i in self.inputs + self.outputs]
+ self.addOutput(PortItem(uniqify('out', names), self, 'output'))
+ def setName(self, name):
+ self.name = name
+ self.label.setPlainText(name)
+ def addInput(self, i):
+ self.inputs.append(i)
+ self.updateSize()
+ def addOutput(self, o):
+ self.outputs.append(o)
+ self.updateSize()
+
+ def contextMenuEvent(self, event):
+ menu = QMenu()
+ pa = menu.addAction('Parameters')
+ pa.triggered.connect(self.editParameters)
+ menu.exec_(event.screenPos())
+ def itemChange(self, change, value):
+ if change == self.ItemSelectedHasChanged:
+ self.sizer.setVisible(value)
+ self.buttonItemAddInput.setVisible(value)
+ self.buttonItemAddOutput.setVisible(value)
+ return super(BlockItem, self).itemChange(change, value)
+
+ def updateSize(self):
+ rect = self.rect()
+ h, w = rect.height(), rect.width()
+ self.buttonItemAddInput.setPos(0, h + 4)
+ self.buttonItemAddOutput.setPos(w+10, h+4)
+ if len(self.inputs) == 1:
+ self.inputs[0].setPos(0.0, h / 2)
+ elif len(self.inputs) > 1:
+ y = 15
+ dy = (h - 30) / (len(self.inputs) - 1)
+ for inp in self.inputs:
+ inp.setPos(0.0, y)
+ y += dy
+ if len(self.outputs) == 1:
+ self.outputs[0].setPos(w, h / 2)
+ elif len(self.outputs) > 1:
+ y = 15
+ dy = (h - 30) / (len(self.outputs) - 1)
+ for outp in self.outputs:
+ outp.setPos(w, y)
+ y += dy
+
+ def changeSize(self, p):
+ """ Resize block function """
+ w, h = p.x(), p.y()
+ # Limit the block size:
+ if h < 20:
+ h = 20
+ if w < 40:
+ w = 40
+ self.setRect(0.0, 0.0, w, h)
+ # center label:
+ rect = self.label.boundingRect()
+ lw, lh = rect.width(), rect.height()
+ lx = (w - lw) / 2
+ ly = (h - lh) / 2
+ self.label.setPos(lx, ly)
+ # Update port positions:
+ self.updateSize()
+ return QPointF(w, h)
+
+class EditorGraphicsView(QGraphicsView):
+ def __init__(self, scene, parent=None):
+ QGraphicsView.__init__(self, scene, parent)
+ self.setDragMode(QGraphicsView.RubberBandDrag)
+ def wheelEvent(self, event):
+ pos = event.pos()
+ posbefore = self.mapToScene(pos)
+ degrees = event.delta() / 8.0
+ sx = (100.0 + degrees) / 100.0
+ self.scale(sx, sx)
+ event.accept()
+ def dragEnterEvent(self, event):
+ if event.mimeData().hasFormat('component/name'):
+ event.accept()
+ def dragMoveEvent(self, event):
+ if event.mimeData().hasFormat('component/name'):
+ event.accept()
+ def dropEvent(self, event):
+ if event.mimeData().hasFormat('component/name'):
+ name = bytes(event.mimeData().data('component/name')).decode()
+ pos = self.mapToScene(event.pos())
+ self.scene().addNewBlock(pos, name)
+
+class LibraryModel(QStandardItemModel):
+ def __init__(self, parent=None):
+ QStandardItemModel.__init__(self, parent)
+ def mimeTypes(self):
+ return ['component/name']
+ def mimeData(self, idxs):
+ mimedata = QMimeData()
+ for idx in idxs:
+ if idx.isValid():
+ txt = self.data(idx, Qt.DisplayRole) # python 3
+ mimedata.setData('component/name', txt)
+ return mimedata
+
+class DiagramScene(QGraphicsScene):
+ """ Save and load and deletion of item"""
+ def __init__(self, parent=None):
+ super(DiagramScene, self).__init__(parent)
+ self.startedConnection = None
+
+ def saveDiagram(self, filename):
+ items = self.items()
+ blocks = [item for item in items if type(item) is BlockItem]
+ connections = [item for item in items if type(item) is Connection]
+
+ doc = md.Document()
+ modelElement = doc.createElement('system')
+ doc.appendChild(modelElement)
+ for block in blocks:
+ blockElement = doc.createElement("block")
+ x, y = block.scenePos().x(), block.scenePos().y()
+ rect = block.rect()
+ w, h = rect.width(), rect.height()
+ blockElement.setAttribute("name", block.name)
+ blockElement.setAttribute("x", str(int(x)))
+ blockElement.setAttribute("y", str(int(y)))
+ blockElement.setAttribute("width", str(int(w)))
+ blockElement.setAttribute("height", str(int(h)))
+ codeNode = doc.createCDATASection(block.code)
+ codeElement = doc.createElement('code')
+ codeElement.appendChild(codeNode)
+ blockElement.appendChild(codeElement)
+ for inp in block.inputs:
+ portElement = doc.createElement("input")
+ portElement.setAttribute("name", inp.name)
+ blockElement.appendChild(portElement)
+ for outp in block.outputs:
+ portElement = doc.createElement("output")
+ portElement.setAttribute("name", outp.name)
+ blockElement.appendChild(portElement)
+ modelElement.appendChild(blockElement)
+ for connection in connections:
+ connectionElement = doc.createElement("connection")
+ fromPort = connection.fromPort.name
+ toPort = connection.toPort.name
+ fromBlock = connection.fromPort.block.name
+ toBlock = connection.toPort.block.name
+ connectionElement.setAttribute("fromBlock", fromBlock)
+ connectionElement.setAttribute("fromPort", fromPort)
+ connectionElement.setAttribute("toBlock", toBlock)
+ connectionElement.setAttribute("toPort", toPort)
+ for via in connection.vias:
+ viaElement = doc.createElement('via')
+ viaElement.setAttribute('x', str(int(via.x())))
+ viaElement.setAttribute('y', str(int(via.y())))
+ connectionElement.appendChild(viaElement)
+ modelElement.appendChild(connectionElement)
+ with open(filename, 'w') as f:
+ f.write(doc.toprettyxml())
+
+ def loadDiagram(self, filename):
+ try:
+ doc = md.parse(filename)
+ except IOError as e:
+ print('{0} not found'.format(filename))
+ return
+ except xml.parsers.expat.ExpatError as e:
+ print('{0}'.format(e))
+ return
+ sysElements = doc.getElementsByTagName('system')
+ blockElements = doc.getElementsByTagName('block')
+ for sysElement in sysElements:
+ blockElements = sysElement.getElementsByTagName('block')
+ for blockElement in blockElements:
+ x = float(blockElement.getAttribute('x'))
+ y = float(blockElement.getAttribute('y'))
+ w = float(blockElement.getAttribute('width'))
+ h = float(blockElement.getAttribute('height'))
+ name = blockElement.getAttribute('name')
+ block = BlockItem(name)
+ self.addItem(block)
+ block.setPos(x, y)
+ block.sizer.setPos(w, h)
+ codeElements = blockElement.getElementsByTagName('code')
+ if codeElements:
+ cn = codeElements[0].childNodes
+ cdatas = [cd for cd in cn if type(cd) is md.CDATASection]
+ if len(cdatas) > 0:
+ block.code = cdatas[0].data
+ # Load ports:
+ portElements = blockElement.getElementsByTagName('input')
+ for portElement in portElements:
+ name = portElement.getAttribute('name')
+ inp = PortItem(name, block, 'input')
+ block.addInput(inp)
+ portElements = blockElement.getElementsByTagName('output')
+ for portElement in portElements:
+ name = portElement.getAttribute('name')
+ outp = PortItem(name, block, 'output')
+ block.addOutput(outp)
+ connectionElements = sysElement.getElementsByTagName('connection')
+ for connectionElement in connectionElements:
+ fromBlock = connectionElement.getAttribute('fromBlock')
+ fromPort = connectionElement.getAttribute('fromPort')
+ toBlock = connectionElement.getAttribute('toBlock')
+ toPort = connectionElement.getAttribute('toPort')
+ viaElements = connectionElement.getElementsByTagName('via')
+ fromPort = self.findPort(fromBlock, fromPort)
+ toPort = self.findPort(toBlock, toPort)
+ connection = Connection(fromPort, toPort)
+ for viaElement in viaElements:
+ x = int(viaElement.getAttribute('x'))
+ y = int(viaElement.getAttribute('y'))
+ connection.addHandle(QPointF(x, y))
+ self.addItem(connection)
+ def findPort(self, blockname, portname):
+ items = self.items()
+ blocks = [item for item in items if type(item) is BlockItem]
+ for block in [b for b in blocks if b.name == blockname]:
+ for port in block.inputs + block.outputs:
+ if port.name == portname:
+ return port
+ def addNewBlock(self, pos, name):
+ blocknames = [item.name for item in self.items() if type(item) is BlockItem]
+ b1 = BlockItem(uniqify(name, blocknames))
+ b1.setPos(pos)
+ self.addItem(b1)
+ def mouseMoveEvent(self, event):
+ if self.startedConnection:
+ pos = event.scenePos()
+ self.startedConnection.setEndPos(pos)
+ super(DiagramScene, self).mouseMoveEvent(event)
+ def mouseReleaseEvent(self, event):
+ if self.startedConnection:
+ items = self.items(event.scenePos())
+ for item in items:
+ if type(item) is PortItem:
+ self.startedConnection.setToPort(item)
+ self.startedConnection = None
+ return
+ self.deleteItem(self.startedConnection)
+ self.startedConnection = None
+ super(DiagramScene, self).mouseReleaseEvent(event)
+ def startConnection(self, port):
+ self.startedConnection = Connection(port, None)
+ pos = port.scenePos()
+ self.startedConnection.setEndPos(pos)
+ self.addItem(self.startedConnection)
+ def deleteItem(self, item=None):
+ if item:
+ if type(item) is BlockItem:
+ for p in item.inputs + item.outputs:
+ if p.connection:
+ self.deleteItem(p.connection)
+ self.removeItem(item)
+ elif type(item) is Connection:
+ item.releasePorts()
+ self.removeItem(item)
+ else:
+ # No item was supplied, try to delete all currently selected items:
+ items = self.selectedItems()
+ connections = [item for item in items if type(item) is Connection]
+ blocks = [item for item in items if type(item) is BlockItem]
+ for item in connections + blocks:
+ self.deleteItem(item)
+
+class DiagramEditor(QWidget):
+ def __init__(self, parent=None):
+ QWidget.__init__(self, parent)
+
+ # Widget layout and child widgets:
+ self.horizontalLayout = QHBoxLayout(self)
+ self.diagramScene = DiagramScene(self)
+ self.loadDiagram = self.diagramScene.loadDiagram
+ self.diagramView = EditorGraphicsView(self.diagramScene, self)
+ self.horizontalLayout.addWidget(self.diagramView)
+
+ testShortcut = QShortcut(QKeySequence("F12"), self)
+ testShortcut.activated.connect(self.test)
+ delShort = QShortcut(QKeySequence.Delete, self)
+ delShort.activated.connect(self.diagramScene.deleteItem)
+
+ def test(self):
+ self.diagramView.rotate(30)
+ self.zoomAll()
+ def save(self):
+ self.diagramScene.saveDiagram('diagram2.usd')
+ def load(self):
+ filename = QFileDialog.getOpenFileName(self)
+ self.diagramScene.loadDiagram(filename)
+ def zoomAll(self):
+ """ zoom to fit all items """
+ rect = self.diagramScene.itemsBoundingRect()
+ self.diagramView.fitInView(rect, Qt.KeepAspectRatio)
+
+class LibraryWidget(QListView):
+ def __init__(self):
+ super(LibraryWidget, self).__init__(None)
+ self.libraryModel = LibraryModel(self)
+ self.libraryModel.setColumnCount(1)
+ # Create an icon with an icon:
+ pixmap = QPixmap(60, 60)
+ pixmap.fill()
+ painter = QPainter(pixmap)
+ painter.fillRect(10, 10, 40, 40, Qt.blue)
+ painter.setBrush(Qt.yellow)
+ painter.drawEllipse(20, 20, 20, 20)
+ painter.end()
+
+ # Fill library:
+ self.libItems = []
+ self.libItems.append( QStandardItem(QIcon(pixmap), 'Block') )
+ self.libItems.append( QStandardItem(QIcon(pixmap), 'Uber Unit') )
+ self.libItems.append( QStandardItem(QIcon(pixmap), 'Device') )
+ for i in self.libItems:
+ self.libraryModel.appendRow(i)
+ self.setModel(self.libraryModel)
+ self.setViewMode(self.IconMode)
+ self.setDragDropMode(self.DragOnly)
+
+class Main(QMainWindow):
+ def __init__(self):
+ super(Main, self).__init__(None)
+ self.editor = DiagramEditor()
+ self.setCentralWidget(self.editor)
+ self.setWindowTitle("Diagram editor")
+ toolbar = self.addToolBar('Tools')
+
+ saveAction = QAction('Save', self)
+ saveAction.setShortcuts(QKeySequence.Save)
+ saveAction.triggered.connect(self.editor.save)
+ toolbar.addAction(saveAction)
+ openAction = QAction('Open', self)
+ openAction.setShortcuts(QKeySequence.Open)
+ openAction.triggered.connect(self.editor.load)
+ toolbar.addAction(openAction)
+ fullScreenAction = QAction('Full screen', self)
+ fullScreenAction.setShortcuts(QKeySequence("F11"))
+ fullScreenAction.triggered.connect(self.toggleFullScreen)
+ toolbar.addAction(fullScreenAction)
+ zoomAction = QAction('Fit in view', self)
+ zoomAction.setShortcuts(QKeySequence('F8'))
+ zoomAction.triggered.connect(self.editor.zoomAll)
+ toolbar.addAction(zoomAction)
+
+ self.library = LibraryWidget()
+ libraryDock = QDockWidget('Library', self)
+ libraryDock.setWidget(self.library)
+ self.addDockWidget(Qt.LeftDockWidgetArea, libraryDock)
+
+ self.editor.loadDiagram('diagram2.usd')
+
+ def toggleFullScreen(self):
+ self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
+ self.editor.zoomAll()
+
+if __name__ == '__main__':
+ if sys.version_info.major != 3:
+ print('Please use python 3.x')
+ sys.exit(1)
+
+ app = QApplication(sys.argv)
+ main = Main()
+ main.show()
+ main.resize(700, 500)
+ main.editor.zoomAll()
+ app.exec_()
+