├── LICENSE.txt ├── Makefile ├── README.md ├── analyzer.py ├── compiler.py ├── ir.py ├── ir_builder.py ├── lexer.py ├── optimizer.py ├── parser.py ├── symtable.py ├── syntree.py └── test-programs ├── elementary ├── arithmetic.expected ├── arithmetic.wend ├── helloworld.expected ├── helloworld.wend ├── int-overflow.expected ├── int-overflow.wend ├── overload.expected ├── overload.wend ├── scope.expected └── scope.wend ├── gfx ├── README.md ├── breakout.c ├── breakout.wend ├── fire.c ├── fire.wend ├── fire_cursed.c ├── mandelbrot.c ├── metaballs.c ├── metaballs.wend ├── race.c └── race.wend ├── nontrivial ├── bitwise.expected ├── bitwise.wend ├── mandelbrot.expected ├── mandelbrot.wend ├── sqrt.expected ├── sqrt.wend ├── trig-hp12c.expected └── trig-hp12c.wend └── simple ├── collatz.expected ├── collatz.wend ├── eight-queens.expected ├── eight-queens.wend ├── fixed-point.expected ├── fixed-point.wend ├── mutual-recursion.expected ├── mutual-recursion.wend ├── popcount.expected ├── popcount.wend ├── sopfr.expected └── sopfr.wend /LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | .ONESHELL: 3 | .SHELLFLAGS := -eu -o pipefail -c 4 | .DELETE_ON_ERROR: 5 | MAKEFLAGS += --warn-undefined-variables 6 | MAKEFLAGS += --no-builtin-rules 7 | 8 | BUILDDIR = build 9 | WENDDIR = test-programs 10 | TESTS = $(shell find $(WENDDIR) -name '*.wend' -not -path '*/gfx/*') 11 | 12 | GFXDIR = $(WENDDIR)/gfx 13 | GFXLL = $(patsubst $(GFXDIR)/%.wend, $(BUILDDIR)/%.ll, $(wildcard $(GFXDIR)/*.wend)) 14 | GFXASM = $(patsubst $(GFXDIR)/%.wend, $(BUILDDIR)/%.s, $(wildcard $(GFXDIR)/*.wend)) 15 | GFXEXE = $(patsubst $(GFXDIR)/%.wend, $(BUILDDIR)/%.exe, $(wildcard $(GFXDIR)/*.wend)) 16 | 17 | .PHONY: all test clean 18 | 19 | all: test gfx 20 | 21 | $(BUILDDIR): 22 | @mkdir -p $(BUILDDIR) 23 | 24 | test: $(BUILDDIR) 25 | @for WEND in $(TESTS) ; do \ 26 | SRCDIR=$$(dirname $$WEND) ; \ 27 | DSTDIR=$$(dirname $$WEND | sed s/$(WENDDIR)/$(BUILDDIR)/) ; \ 28 | mkdir -p $$DSTDIR ; \ 29 | echo -n Testing $$WEND... ;\ 30 | EXP=$$(echo $$WEND|sed s/\.wend/\.expected/) ; \ 31 | LL=$$DSTDIR/$$(basename $$WEND|sed s/\.wend/\.ll/) ; \ 32 | ASM=$$DSTDIR/$$(basename $$WEND|sed s/\.wend/\.s/) ; \ 33 | ELF=$$DSTDIR/$$(basename $$WEND|sed s/\.wend//) ; \ 34 | python3 compiler.py $$WEND > $$LL ; \ 35 | llc $$LL ; \ 36 | gcc $$ASM -o $$ELF ; \ 37 | $$ELF | diff $$EXP - ; \ 38 | echo ' ok' ; \ 39 | done 40 | 41 | $(BUILDDIR)/%.exe: $(BUILDDIR)/%.s 42 | gcc -o $@ $< 43 | 44 | $(BUILDDIR)/%.s: $(BUILDDIR)/%.ll 45 | llc $< 46 | 47 | $(BUILDDIR)/%.ll: $(GFXDIR)/%.wend 48 | python3 compiler.py $< > $@ 49 | 50 | gfx: $(BUILDDIR) $(GFXLL) $(GFXASM) $(GFXEXE) 51 | 52 | clean: 53 | rm -rf $(BUILDDIR) 54 | find . -type f -name *.pyc -delete 55 | find . -type d -name __pycache__ -delete 56 | rm -f *png 57 | rm -f *ll 58 | rm -f *dot 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Can I make an *optimizing* compiler under 1k lines of code? 2 | ## N.B.: Under construction! This is a fork of my tinycompiler, and I have no idea where it is going. 3 | 4 | Current description is available here: 5 | [![](https://ssloy.github.io/tinyoptimizer/home/tinyoptimizer.png)](https://ssloy.github.io/tinyoptimizer/) 6 | 7 | Here I am playing with code optimizations, if you are interested in lexer/parser/symbol tables and assembly generation, check the [tinycompiler](https://github.com/ssloy/tinycompiler). 8 | -------------------------------------------------------------------------------- /analyzer.py: -------------------------------------------------------------------------------- 1 | from syntree import * 2 | from symtable import * 3 | 4 | def decorate(ast): 5 | if not isinstance(ast, Function) or ast.name != 'main' or ast.deco['type'] != Type.VOID or len(ast.args)>0: 6 | raise Exception('Cannot find a valid entry point') 7 | symtable = SymbolTable() 8 | symtable.add_fun(ast.name, [], ast.deco) 9 | ast.deco['strings'] = set() # collection of constant strings from the program 10 | process_scope(ast, symtable) # N.B.: the decoration is made in two passes, necessary to propagate context to the function calls 11 | process_scope(ast, symtable) # for the functions defined later on 12 | 13 | def process_scope(fun, symtable): 14 | symtable.push_scope(fun.deco) 15 | for v in fun.args: # process function arguments 16 | symtable.add_var(v.name, v.deco) 17 | for v in fun.var: # process local variables 18 | symtable.add_var(v.name, v.deco) 19 | for f in fun.fun: # process nested functions: first add function symbols to the table 20 | symtable.add_fun(f.name, [v.deco['type'] for v in f.args], f.deco) 21 | for f in fun.fun: # then process nested function bodies 22 | process_scope(f, symtable) 23 | for s in fun.body: # process the list of statements 24 | process_instruction(s, symtable) 25 | symtable.pop_scope() 26 | 27 | def process_instruction(n, symtable): 28 | match n: 29 | case Print(): # no type checking is necessary 30 | process_instruction(n.expr, symtable) 31 | case Return(): 32 | if n.expr is None: return # TODO semantic check for return; in non-void functions 33 | process_instruction(n.expr, symtable) 34 | if symtable.ret_stack[-1]['type'] != n.expr.deco['type']: 35 | raise Exception('Incompatible types in return statement, line %s', n.deco['lineno']) 36 | case Assign(): 37 | process_instruction(n.expr, symtable) 38 | n.deco |= symtable.find_var(n.name) 39 | if n.deco['type'] != n.expr.deco['type']: 40 | raise Exception('Incompatible types in assignment statement, line %s', n.deco['lineno']) 41 | case While(): 42 | process_instruction(n.expr, symtable) 43 | if n.expr.deco['type'] != Type.BOOL: 44 | raise Exception('Non-boolean expression in while statement, line %s', n.deco['lineno']) 45 | for s in n.body: 46 | process_instruction(s, symtable) 47 | case IfThenElse(): 48 | process_instruction(n.expr, symtable) 49 | if n.expr.deco['type'] != Type.BOOL: 50 | raise Exception('Non-boolean expression in if statement, line %s', n.deco['lineno']) 51 | for s in n.ibody + n.ebody: 52 | process_instruction(s, symtable) 53 | case ArithOp(): 54 | process_instruction(n.left, symtable) 55 | process_instruction(n.right, symtable) 56 | if n.left.deco['type'] != Type.INT or n.right.deco['type'] != Type.INT: 57 | raise Exception('Arithmetic operation over non-integer type in line %s', n.deco['lineno']) 58 | case LogicOp(): 59 | process_instruction(n.left, symtable) 60 | process_instruction(n.right, symtable) 61 | if (n.left.deco['type'] != n.right.deco['type']) or \ 62 | (n.op in ['<=', '<', '>=', '>'] and n.left.deco['type'] != Type.INT) or \ 63 | (n.op in ['&&', '||'] and n.left.deco['type'] != Type.BOOL): 64 | raise Exception('Boolean operation over incompatible types in line %s', n.deco['lineno']) 65 | case Var(): # no type checking is necessary 66 | n.deco |= symtable.find_var(n.name) 67 | case FunCall(): 68 | for s in n.args: 69 | process_instruction(s, symtable) 70 | n.deco |= symtable.find_fun(n.name, [ a.deco['type'] for a in n.args ]) 71 | case String(): # no type checking is necessary 72 | symtable.ret_stack[1]['strings'].add((n.deco['label'], n.value)) 73 | case Integer() | Boolean(): pass # no type checking is necessary 74 | case other: raise Exception('Unknown instruction', n) 75 | -------------------------------------------------------------------------------- /compiler.py: -------------------------------------------------------------------------------- 1 | import io, sys 2 | from lexer import WendLexer 3 | from parser import WendParser 4 | from analyzer import decorate 5 | from ir_builder import build_ir 6 | from optimizer import * 7 | 8 | if len(sys.argv)!=2: 9 | sys.exit('Usage: compiler.py path/source.wend') 10 | if True: 11 | #try: 12 | f = open(sys.argv[1], 'r') 13 | tokens = WendLexer().tokenize(f.read()) 14 | ast = WendParser().parse(tokens) 15 | decorate(ast) 16 | ir = build_ir(ast) 17 | 18 | for cfg in ir.fun: 19 | mem2reg(cfg) 20 | 21 | print(ir) 22 | #except Exception as e: 23 | # print(e) 24 | -------------------------------------------------------------------------------- /ir.py: -------------------------------------------------------------------------------- 1 | class Instruction: 2 | def __init__(self, string, *args): 3 | self.string, self.op = string, args # string and parameters (typically three) 4 | 5 | def __repr__(self): 6 | return self.string.format(op = self.op) 7 | 8 | class Phi: 9 | def __init__(self, reg, t, choice=None): 10 | self.reg, self.t, self.choice = reg, t, choice or {} 11 | 12 | def __repr__(self): 13 | choice = ', '.join([ '[{percent}{value}, %{block}]'.format(percent = '%' if type(value) is str else '', value=value, block=block) for block, value in self.choice.items() ]) 14 | return f'%{self.reg} = phi {self.t} {choice}' 15 | 16 | class BasicBlock: 17 | def __init__(self, label): 18 | self.label = label 19 | self.phi_functions, self.instructions = [], [] 20 | self.successors, self.predecessors = set(), set() 21 | 22 | def find_and_replace(self, find, replace): 23 | for i in self.instructions: 24 | i.op = list( replace if s==find else s for s in i.op ) 25 | 26 | def __repr__(self): 27 | return f'{self.label}:\n' + \ 28 | ''.join([ f'\t{phi}\n' for phi in self.phi_functions ]) + \ 29 | ''.join([ f'\t{i}\n' for i in self.instructions ]) 30 | 31 | class ControlFlowGraph: # a control flow graph is made of basic blocks and 32 | def __init__(self, header, footer): # header + footer strings to form a valid LLVM IR program 33 | self.header, self.footer, self.blocks, self.last = header, footer, {}, None 34 | 35 | def add_block(self, label): 36 | self.blocks[label] = BasicBlock(label) 37 | self.last = self.blocks[label] # the last block added to the CFG, heavily used by the IR builder 38 | 39 | def __repr__(self): 40 | return f'{self.header}\n' + \ 41 | ''.join([ f'{block}' for block in self.blocks.values() ]) + \ 42 | f'{self.footer}\n' 43 | 44 | def find_and_replace(self, find, replace): 45 | for b in self.blocks.values(): 46 | b.find_and_replace(find, replace) 47 | 48 | def compute_adjacency(self): 49 | for b1 in self.blocks.values(): 50 | if any('unreachable' in i.string or 'ret ' in i.string for i in b1.instructions): 51 | continue # even if there is a br instruction later on in the block, there are no outgoing edges 52 | for succ in b1.instructions[-1].op[int('br i1' in b1.instructions[-1].string):]: 53 | b2 = self.blocks[succ] 54 | b1.successors.add(b2) 55 | b2.predecessors.add(b1) 56 | 57 | class IR: 58 | def __init__(self, prog): # self.prog is an entry point string + data section 59 | self.prog, self.fun = prog, [] # self.fun is a list of control flow graphs (one per function) 60 | 61 | def __repr__(self): 62 | return self.prog + ''.join([str(f) for f in self.fun]) 63 | -------------------------------------------------------------------------------- /ir_builder.py: -------------------------------------------------------------------------------- 1 | from syntree import * 2 | from ir import * 3 | 4 | def build_ir(n): 5 | data = ''.join(['@{label} = constant [{size} x i8] c"{string}\\00"\n'.format( 6 | label = label, 7 | size = len(string)+1, # +1 for null termination 8 | string = ''.join(['\\%02x' % ord(x) for x in string]) # hex escape the string 9 | ) for label,string in n.deco['strings']]) 10 | entry = n.deco['label'] 11 | prog = f'''declare i32 @printf(i8*, ...) 12 | @newline = constant [ 2 x i8] c"\\0a\\00" 13 | @integer = constant [ 3 x i8] c"%d\\00" 14 | @string = constant [ 3 x i8] c"%s\\00" 15 | @bool = constant [11 x i8] c"false\\00true\\00" 16 | {data} 17 | define i32 @main() {{ 18 | call void @{entry}() 19 | ret i32 0 20 | }} 21 | ''' 22 | ir = IR(prog) 23 | fun(n, ir) 24 | return ir 25 | 26 | def lltype(t): 27 | return ['void', 'i32', 'i1'][t] 28 | 29 | def fun(n, ir): 30 | label = n.deco['label'] 31 | args = ', '.join([ '{t}* %{n}' .format(n = a[0], t = lltype(a[1])) for a in n.deco['nonlocal'] ] + 32 | [ '{t} %{n}.arg'.format(n = a.deco['fullname'], t = lltype(a.deco['type'])) for a in n.args ]) 33 | rettype = lltype(n.deco['type']) 34 | cfg = ControlFlowGraph(f'define {rettype} @{label}({args}) {{', '}') 35 | cfg.add_block('entry') 36 | for v in n.args: 37 | cfg.last.instructions += [ Instruction('%{{op[0]}} = alloca {t}'.format(t=lltype(v.deco['type'])), v.deco['fullname']) ] 38 | cfg.last.instructions += [ Instruction('store {t} %{{op[0]}}, {t}* %{{op[1]}}'.format(t=lltype(v.deco['type'])), f'{v.deco["fullname"]}.arg', v.deco['fullname']) ] 39 | for v in n.var: 40 | cfg.last.instructions += [ Instruction('%{{op[0]}} = alloca {t}'.format(t=lltype(v.deco['type'])), v.deco['fullname']) ] 41 | cfg.last.instructions += [ Instruction('store {t} {{op[0]}}, {t}* %{{op[1]}}'.format(t=lltype(v.deco['type'])), 0, v.deco['fullname']) ] # zero initialized variables 42 | for f in n.fun: fun(f, ir) 43 | for s in n.body: stat(s, cfg) 44 | cfg.last.instructions += [ Instruction('ret void' if n.deco['type']==Type.VOID else 'unreachable') ] 45 | cfg.compute_adjacency() 46 | ir.fun.append(cfg) 47 | 48 | def stat(n, cfg): 49 | match n: 50 | case Print(): 51 | match n.expr.deco['type']: 52 | case Type.INT: 53 | stat(n.expr, cfg) 54 | cfg.last.instructions += [ Instruction('call i32 (i8*, ...)* @printf(ptr @integer, i32 %{op[0]})', LabelFactory.cur_label()) ] 55 | case Type.BOOL: 56 | stat(n.expr, cfg) 57 | label1 = LabelFactory.cur_label() 58 | label2 = LabelFactory.new_label() 59 | cfg.last.instructions += [ Instruction(f'%{label2}.offset = select i1 %{{op[0]}}, i32 6, i32 0', label1), 60 | Instruction(f'%{label2}.ptr = getelementptr [11 x i8], ptr @bool, i32 0, i32 %{label2}.offset'), 61 | Instruction(f'call i32 (i8*, ...) @printf(ptr %{label2}.ptr)') ] 62 | case Type.STRING: 63 | label = n.expr.deco['label'] 64 | cfg.last.instructions += [ Instruction(f'call i32 (i8*, ...) @printf(ptr @string, ptr @{label})') ] 65 | case other: raise Exception('Unknown expression type', n.expr) 66 | if n.newline: 67 | cfg.last.instructions += [ Instruction('call i32 (i8*, ...) @printf(ptr @newline)') ] 68 | case Return(): 69 | if n.expr: 70 | stat(n.expr, cfg) 71 | t = lltype(n.expr.deco['type']) 72 | cfg.last.instructions += [ Instruction(f'ret {t} %{{op[0]}}', LabelFactory.cur_label()) ] 73 | else: 74 | pass 75 | cfg.last.instructions += [ Instruction('ret void') ] 76 | case Assign(): 77 | stat(n.expr, cfg) 78 | t = lltype(n.deco['type']) 79 | cfg.last.instructions += [ Instruction(f'store {t} %{{op[0]}}, {t}* %{{op[1]}}', LabelFactory.cur_label(), n.deco['fullname']) ] 80 | case Integer(): 81 | cfg.last.instructions += [ Instruction('%{op[0]} = add i32 {op[1]}, {op[2]}', LabelFactory.new_label(), 0, n.value) ] 82 | case Boolean(): 83 | cfg.last.instructions += [ Instruction('%{op[0]} = or i1 {op[1]}, {op[2]}', LabelFactory.new_label(), 0, int(n.value)) ] 84 | case Var(): 85 | t = lltype(n.deco['type']) 86 | cfg.last.instructions += [ Instruction(f'%{{op[0]}} = load {t}, {t}* %{{op[1]}}', LabelFactory.new_label(), n.deco['fullname']) ] 87 | case ArithOp() | LogicOp(): 88 | stat(n.left, cfg) 89 | left = LabelFactory.cur_label() 90 | stat(n.right, cfg) 91 | right = LabelFactory.cur_label() 92 | t = lltype(n.left.deco['type']) 93 | op = {'+':'add', '-':'sub', '*':'mul', '/':'sdiv', '%':'srem','||':'or', '&&':'and','<=':'icmp sle', '<':'icmp slt', '>=':'icmp sge', '>':'icmp sgt', '==':'icmp eq', '!=':'icmp ne'}[n.op] 94 | cfg.last.instructions += [ Instruction(f'%{{op[0]}} = {op} {t} %{{op[1]}}, %{{op[2]}}', LabelFactory.new_label(), left, right) ] 95 | case FunCall(): 96 | pointers = ', '.join([ '{ptype}* %{pname}'.format(ptype=lltype(ptype), pname=pname) for pname, ptype in n.deco['nonlocal'] ]) # external variable pointers 97 | argline = ', '.join(['{t} %{{op[{cnt}]}}'.format(t=lltype(e.deco['type']), cnt=str(cnt)) for cnt,e in enumerate(n.args) ]) # actual function arguments 98 | if pointers and argline: pointers += ', ' 99 | argnames = [] 100 | for e in n.args: 101 | stat(e, cfg) 102 | argnames.append(LabelFactory.cur_label()) 103 | offset = len(argnames) 104 | if n.deco['type']==Type.VOID: 105 | cfg.last.instructions += [ Instruction(f'call void @{{op[{offset}]}}({pointers}{argline})', *argnames, n.deco['label']) ] 106 | else: 107 | t = lltype(n.deco['type']) 108 | cfg.last.instructions += [ Instruction(f'%{{op[{offset}]}} = call {t} @{{op[{offset+1}]}}({pointers}{argline})', *argnames, LabelFactory.new_label(), n.deco['label']) ] 109 | case IfThenElse(): 110 | stat(n.expr, cfg) 111 | label1 = LabelFactory.cur_label() 112 | label2 = LabelFactory.new_label() 113 | cfg.last.instructions += [ Instruction('br i1 %{op[0]}, label %{op[1]}, label %{op[2]}', label1, label2 + '.then', label2 + '.else') ] 114 | cfg.add_block(label2 + '.then') 115 | for s in n.ibody: 116 | stat(s, cfg) 117 | cfg.last.instructions += [ Instruction('br label %{op[0]}', label2 + '.end') ] 118 | cfg.add_block(label2 + '.else') 119 | for s in n.ebody: 120 | stat(s, cfg) 121 | cfg.last.instructions += [ Instruction('br label %{op[0]}', label2 + '.end') ] 122 | cfg.add_block(label2 + '.end') 123 | case While(): 124 | label = LabelFactory.new_label() 125 | cfg.last.instructions += [ Instruction('br label %{op[0]}', label + '.cond') ] 126 | cfg.add_block(label + '.cond') 127 | stat(n.expr, cfg) 128 | cfg.last.instructions += [ Instruction('br i1 %{op[0]}, label %{op[1]}, label %{op[2]}', LabelFactory.cur_label(), label + '.body', label + '.end') ] 129 | cfg.add_block(label + '.body') 130 | for s in n.body: 131 | stat(s, cfg) 132 | cfg.last.instructions += [ Instruction('br label %{op[0]}', label + '.cond') ] 133 | cfg.add_block(label + '.end') 134 | case other: raise Exception('Unknown instruction', n) 135 | -------------------------------------------------------------------------------- /lexer.py: -------------------------------------------------------------------------------- 1 | class Token: 2 | def __init__(self, t, v, l=None): 3 | self.type, self.value, self.lineno = t, v, (l or 0) 4 | 5 | def __repr__(self): 6 | return f'Token(type={self.type!r}, value={self.value!r}, lineno={self.lineno!r})' 7 | 8 | class WendLexer: 9 | keywords = {'true':'BOOLEAN','false':'BOOLEAN','print':'PRINT','println':'PRINT','int':'TYPE','bool':'TYPE','if':'IF','else':'ELSE','while':'WHILE','return':'RETURN'} 10 | double_char = {'==':'COMP', '<=':'COMP', '>=':'COMP', '!=':'COMP', '&&':'AND', '||':'OR'} 11 | single_char = {'=':'ASSIGN','<':'COMP', '>':'COMP', '!':'NOT', '+':'PLUS', '-':'MINUS', '/':'DIVIDE', '*':'TIMES', '%':'MOD','(':'LPAREN',')':'RPAREN', '{':'BEGIN', '}':'END', ';':'SEMICOLON', ',':'COMMA'} 12 | tokens = {'ID', 'STRING', 'INTEGER'} | { v for k, v in keywords.items() | double_char.items() | single_char.items() } 13 | 14 | def tokenize(self, text): 15 | lineno, idx, state, accum = 0, 0, 0, '' 16 | while idx1, self.reachable): # iterate through junction points among reachable blocks 38 | for p in b.predecessors: 39 | runner = p 40 | while runner != idom[b]: 41 | df[runner].add(b) 42 | runner = idom[runner] 43 | return df 44 | 45 | class mem2reg(Optimizer): 46 | def __init__(self, cfg): 47 | super().__init__(cfg) 48 | self.allocas = self.remove_promotable_allocas() 49 | self.place_phi() 50 | stack = [{}] # stack of maps (variable -> value); necessary for storing context while branching 51 | self.remove_store_load(self.entry, None, set(), stack) 52 | 53 | def remove_promotable_allocas(self): 54 | allocas = { i.op[0] : i.string.split()[-1] for i in self.entry.instructions if 'alloca' in i.string } # find all allocas in the entry block 55 | for v in allocas.copy().keys(): # for every variable 56 | for b in self.reachable: 57 | for i in b.instructions: # for every instruction in the CFG 58 | if f'* %{v}' in i.string: # if we find a funcall with a reference to the variable 59 | allocas.pop(v, None) # then the variable is not promotable 60 | for i in self.entry.instructions[:]: 61 | if 'alloca' in i.string and i.op[0] in allocas: 62 | self.entry.instructions.remove(i) # remove promotable allocas 63 | return allocas # variable name -> type dictionary 64 | 65 | def place_phi(self): # Fig 9.9b in [Cooper and Torczon, Engineering a Compiler Broché] 66 | df = self.dominance_frontiers() 67 | phi_places = { v:set() for v in self.allocas.keys() } # map (variable -> set of basic blocks) 68 | for v in self.allocas.keys(): 69 | blocks_with_store = { b for b in self.reachable for i in b.instructions if 'store' in i.string and i.op[1]==v } 70 | blocks_to_consider = blocks_with_store.copy() 71 | while blocks_to_consider: 72 | block = blocks_to_consider.pop() 73 | for frontier in df[block]: 74 | if frontier in phi_places[v]: continue 75 | phi_places[v].add(frontier) 76 | blocks_to_consider.add(frontier) 77 | for v, bb in phi_places.items(): # insert phi nodes (for the moment without choices) 78 | for b in bb: 79 | b.phi_functions.append(Phi(v + '_' + b.label, self.allocas[v])) 80 | 81 | def remove_store_load(self, block, prev, visited, stack): 82 | def find_variable(v, stack): # walk the stack back until 83 | for frame in reversed(stack): # the current variable instance is found 84 | if v in frame: return frame[v] # N.B. we suppose that the variables were initialized explicitly 85 | 86 | for phi in block.phi_functions: # place phi node choice for the current path 87 | v = phi.reg[:-len(block.label)-1] # phi saves the choice into a register named foo_bar, where foo is the name of the variable, and bar is the name of the basic block 88 | val = find_variable(v, stack) 89 | phi.choice[prev.label] = val 90 | stack[-1][v] = phi.reg 91 | 92 | if block in visited: return # we need to revisit blocks with phi functions as many times as we have incoming edges, 93 | visited.add(block) # therefore the visited check is made after the choice placement 94 | 95 | for i in block.instructions[:]: # iterate through a copy since we modify the list 96 | if 'load' in i.string and i.op[1] in self.allocas: 97 | val = find_variable(i.op[1], stack) 98 | block.instructions.remove(i) 99 | block.find_and_replace(i.op[0], val) 100 | elif 'store' in i.string and i.op[1] in self.allocas: 101 | stack[-1][i.op[1]] = i.op[0] 102 | block.instructions.remove(i) 103 | elif 'br i1' in i.string: 104 | stack.append({}) 105 | self.remove_store_load(self.cfg.blocks[i.op[1]], block, visited, stack) 106 | stack.pop() 107 | stack.append({}) 108 | self.remove_store_load(self.cfg.blocks[i.op[2]], block, visited, stack) 109 | stack.pop() 110 | elif 'br label' in i.string: 111 | self.remove_store_load(self.cfg.blocks[i.op[0]], block, visited, stack) 112 | -------------------------------------------------------------------------------- /parser.py: -------------------------------------------------------------------------------- 1 | from lexer import WendLexer 2 | from syntree import * 3 | 4 | class ParseState: 5 | def __init__(self, rule, dot, start, token = None, prev = None): 6 | self.rule = rule # index of the parse rule in the grammar 7 | self.dot = dot # index of next symbol in the rule (dot position) 8 | self.start = start # we saw this many tokens when we started the rule 9 | self.token = token # we saw this many tokens up to the current dot position # these two members are not necessary for 10 | self.prev = prev # parent parse state pointer # the recogninzer, but are handy to retrieve a parse path 11 | 12 | def next_symbol(self): 13 | prod = WendParser.grammar[self.rule][1] 14 | return prod[self.dot] if self.dot0: # chomp those symbols from the stack 118 | chew = stack[-chomp:] 119 | del stack[-chomp:] 120 | stack.append(self.grammar[rule.rule][2](chew)) # put AST node back on the stack 121 | return stack[0] # normally we have only one symbol left on the stack 122 | 123 | def parse(self, tokens): 124 | return self.build_syntree( self.recognize(tokens) ) 125 | -------------------------------------------------------------------------------- /symtable.py: -------------------------------------------------------------------------------- 1 | class SymbolTable(): 2 | def __init__(self): 3 | self.variables = [{}] # stack of variable symbol tables 4 | self.functions = [{}] # stack of function symbol tables 5 | self.ret_stack = [ None ] # stack of enclosing function symbols, useful for return statements 6 | 7 | def add_fun(self, name, argtypes, deco): # a function can be identified by its name and a list of argument types, e.g. 8 | signature = (name, *argtypes) # fun foo(x:bool, y:int) : int {...} has ('foo',Type.BOOL,Type.INT) signature 9 | if signature in self.functions[-1]: 10 | raise Exception('Double declaration of the function %s %s' % (signature[0], signature[1:])) 11 | self.functions[-1][signature] = deco 12 | 13 | def add_var(self, name, deco): 14 | if name in self.variables[-1]: 15 | raise Exception('Double declaration of the variable %s' % name) 16 | self.variables[-1][name] = deco 17 | deco['fullname'] = self.ret_stack[-1]['label'] + '.' + name 18 | 19 | def push_scope(self, deco): 20 | deco['nonlocal'] = set() # set of nonlocal variable names in the function body 21 | self.variables.append({}) 22 | self.functions.append({}) 23 | self.ret_stack.append(deco) 24 | 25 | def pop_scope(self): 26 | self.variables.pop() 27 | self.functions.pop() 28 | self.ret_stack.pop() 29 | 30 | def find_var(self, name): 31 | try: 32 | for i in reversed(range(len(self.variables))): # find the variable symbol 33 | if name in self.variables[i]: break 34 | deco = self.variables[i][name] 35 | for i in range(i+1, len(self.variables)): # propagate the pointer to the current context 36 | self.ret_stack[i]['nonlocal'].add((deco['fullname'], deco['type'])) 37 | return deco 38 | except KeyError: raise Exception('No declaration for the variable %s' % name) 39 | 40 | def find_fun(self, name, argtypes): 41 | try: 42 | signature = (name, *argtypes) 43 | for i in reversed(range(len(self.functions))): # find the function symbol 44 | if signature in self.functions[i]: break 45 | deco = self.functions[i][signature] 46 | if 'nonlocal' in deco: # propagate context pointers to the function call 47 | for fullname,t in deco['nonlocal']: 48 | label = fullname.partition('.')[0] 49 | for f in reversed(self.ret_stack): 50 | if f['label'] == label: break 51 | f['nonlocal'].add((fullname, t)) 52 | return deco 53 | except KeyError: raise Exception('No declaration for the function %s' % signature[0], signature[1:]) 54 | -------------------------------------------------------------------------------- /syntree.py: -------------------------------------------------------------------------------- 1 | class LabelFactory: # this is a suffix to add to all function names 2 | counter = 0 # in particular, it is useful for function overloading 3 | @staticmethod # it is also useful for different goto labels (loops, conditional statements etc) in assembly code 4 | def cur_label(): 5 | return "uniqstr%d" % LabelFactory.counter 6 | def new_label(): 7 | LabelFactory.counter += 1 8 | return "uniqstr%d" % LabelFactory.counter 9 | 10 | class Type: 11 | VOID = 0 12 | INT = 1 13 | BOOL = 2 14 | STRING = 3 15 | 16 | class Function: 17 | def __init__(self, name, args, var, fun, body, deco): 18 | self.name = name # function name, string 19 | self.args = args # function arguments, list of tuples (name, type) 20 | self.var = var # local variables, list of tuples (name, type) 21 | self.fun = fun # nested functions, list of Function nodes 22 | self.body = body # function body, list of statement nodes (Print/Return/Assign/While/IfThenElse/FunCall) 23 | self.deco = deco | {'label' : name+'_'+LabelFactory.new_label()} # decoration dictionary to be filled by the parser (line number) and by the semantic analyzer (return type, scope id etc) 24 | 25 | # statements 26 | class Print: 27 | def __init__(self, expr, newline, deco): 28 | self.expr, self.newline, self.deco = expr, newline, deco 29 | 30 | class Return: 31 | def __init__(self, expr, deco): 32 | self.expr, self.deco = expr, deco 33 | 34 | class Assign: 35 | def __init__(self, name, expr, deco): 36 | self.name, self.expr, self.deco = name, expr, deco 37 | 38 | class While: 39 | def __init__(self, expr, body, deco): 40 | self.expr, self.body, self.deco = expr, body, deco 41 | 42 | class IfThenElse: 43 | def __init__(self, expr, ibody, ebody, deco): 44 | self.expr, self.ibody, self.ebody, self.deco = expr, ibody, ebody, deco 45 | 46 | # expressions 47 | class ArithOp: 48 | def __init__(self, op, left, right, deco): 49 | self.op, self.left, self.right, self.deco = op, left, right, deco | {'type' : Type.INT} 50 | 51 | class LogicOp: 52 | def __init__(self, op, left, right, deco): 53 | self.op, self.left, self.right, self.deco = op, left, right, deco | {'type' : Type.BOOL} 54 | 55 | class Integer: 56 | def __init__(self, value, deco): 57 | self.value, self.deco = value, deco | {'type' : Type.INT} 58 | 59 | class Boolean: 60 | def __init__(self, value, deco): 61 | self.value, self.deco = value, deco | {'type' : Type.BOOL} 62 | 63 | class String: 64 | def __init__(self, value, deco): 65 | self.value, self.deco = value, deco | {'type' : Type.STRING, 'label' : LabelFactory.new_label() } 66 | 67 | class Var: 68 | def __init__(self, name, deco): 69 | self.name, self.deco = name, deco 70 | 71 | class FunCall: # depending on the context, a function call can be a statement or an expression 72 | def __init__(self, name, args, deco): 73 | self.name, self.args, self.deco = name, args, deco 74 | -------------------------------------------------------------------------------- /test-programs/elementary/arithmetic.expected: -------------------------------------------------------------------------------- 1 | -7 2 | -17 3 | 1 4 | 15 5 | false 6 | true 7 | true 8 | false 9 | true 10 | false 11 | true 12 | -------------------------------------------------------------------------------- /test-programs/elementary/arithmetic.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | println +3 + 5 * -2; 3 | println 3 - 4 * 5; 4 | println 3 - 10 / 5; 5 | println (-2+3*4)+5/(7-6)%8; 6 | println 5<3; 7 | println 3==3; 8 | println 3 * (4 + 5) / 7 == 3; 9 | println true && false; 10 | println true && false || true; 11 | println !true; 12 | println 3<=3 && 3>=3; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /test-programs/elementary/helloworld.expected: -------------------------------------------------------------------------------- 1 | hello //world 2 | -------------------------------------------------------------------------------- /test-programs/elementary/helloworld.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | println "hello //world"; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test-programs/elementary/int-overflow.expected: -------------------------------------------------------------------------------- 1 | -2147483648 2 | 2147483647 3 | -------------------------------------------------------------------------------- /test-programs/elementary/int-overflow.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | println 256*256*256*128; // one overflow 3 | println 256*256*256*128 - 1; // two overflows 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test-programs/elementary/overload.expected: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | -------------------------------------------------------------------------------- /test-programs/elementary/overload.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int main(int x) { 3 | return x; 4 | } 5 | 6 | int main(int x, int y) { 7 | return x + y; 8 | } 9 | 10 | int main(bool x) { 11 | if x { 12 | return 0; 13 | } 14 | return 2; 15 | } 16 | 17 | println main(0); 18 | println main(0, 1); 19 | println main(false); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /test-programs/elementary/scope.expected: -------------------------------------------------------------------------------- 1 | 3 2 | 2 3 | -------------------------------------------------------------------------------- /test-programs/elementary/scope.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int x; 3 | 4 | h() { 5 | int x; 6 | f(0); 7 | } 8 | 9 | f(int y) { 10 | g(int y) { 11 | x = y + 1; 12 | } 13 | 14 | x = y; 15 | g(x + 1); 16 | } 17 | 18 | x = 0; 19 | f(x + 1); 20 | println x; 21 | h(); 22 | println x; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /test-programs/gfx/README.md: -------------------------------------------------------------------------------- 1 | # Graphics! 2 | It is so dull to compute Fibonacci numbers, so here are more eyecandy examples for tinycompiler. 3 | 4 | ### build wend version: 5 | ```sh 6 | git clone https://github.com/ssloy/tinycompiler.git && 7 | cd tinycompiler && 8 | make gfx 9 | ``` 10 | 11 | ### build C version: 12 | ```sh 13 | git clone https://github.com/ssloy/tinycompiler.git && 14 | cd tinycompiler/test-programs/gfx/ && 15 | gcc breakout.c -o breakout && 16 | gcc fire.c -o fire && 17 | gcc mandelbrot.c -o mandelbrot && 18 | gcc metaballs.c -o metaballs -lm && 19 | gcc race.c -o race -lm 20 | ``` 21 | 22 | 23 | ### Mandelbrot set 24 | 25 | 26 | ### Zero-player breakout game 27 | ![](https://raw.githubusercontent.com/ssloy/ssloy.github.io/main/docs/tinycompiler/gfx/breakout.gif) 28 | 29 | ### Fire 30 | ![](https://raw.githubusercontent.com/ssloy/ssloy.github.io/main/docs/tinycompiler/gfx/fire6.gif) 31 | 32 | ### Sunset race 33 | ![](https://raw.githubusercontent.com/ssloy/ssloy.github.io/main/docs/tinycompiler/gfx/sunset-race.gif) 34 | 35 | ### Metaballs 36 | ![](https://raw.githubusercontent.com/ssloy/ssloy.github.io/main/docs/tinycompiler/gfx/metaballs.gif) 37 | 38 | -------------------------------------------------------------------------------- /test-programs/gfx/breakout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // for usleep() 4 | 5 | #define ABS(a) (((a) < 0) ? -(a) : (a)) 6 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 7 | 8 | const char* palette[2] = { "187;204;51", "213;48;49" }; 9 | struct Ball { int x[2], v[2]; } balls[2]; 10 | bool bricks[48*48]; // The battlefield. The brick grid is 16x16, but it is zoomed x4 for more fluid motion 11 | 12 | int xor_bricks(int x, int y, bool ball) { // flip a 4x4 block of the battlefield 13 | int hit = -1; // N.B. the block is not aligned to the brick grid 14 | for (int i=0; i<16; i++) { 15 | int idx = x+i%4+(y+i/4)*48; 16 | if ((bricks[idx] ^= true) == ball) hit = idx; // if a ball hits a brick, return the brick position 17 | } 18 | return hit; 19 | } 20 | 21 | int main() { 22 | balls[0].x[0] = 3; balls[0].x[1] = 3; // initial position and speed for two balls 23 | balls[0].v[0] = 1; balls[0].v[1] = 3; 24 | balls[1].x[0] = 41; balls[1].x[1] = 43; 25 | balls[1].v[0] = -1; balls[1].v[1] = -3; 26 | 27 | for (int i=0; i<48*48; i++) 28 | bricks[i] = i<48*48/2; // initialize the battlefield 29 | 30 | printf("\033[2J"); // clear screen 31 | for (;;) { 32 | printf("\033[H"); // home 33 | for (int b=0; b<2; b++) // for each ball 34 | for (int d=0; d<2; d++ ) // for each coordinate 35 | for (int i=0; i 0 ? 1 : -1; 37 | if (balls[b].x[d]<0 || balls[b].x[d]>48-4) { // bounce the ball off the walls 38 | balls[b].x[d] = CLAMP(balls[b].x[d], 0, 48-4); 39 | balls[b].v[d] = -balls[b].v[d]; 40 | } 41 | int hit = xor_bricks(balls[b].x[0], balls[b].x[1], !b); // draw the ball and check if it hits a brick 42 | xor_bricks(balls[b].x[0], balls[b].x[1], !b); // immediately clear the ball 43 | if (hit!=-1) { // if we hit a brick 44 | xor_bricks(((hit%48)/4)*4, ((hit/48)/4)*4, !b); // snap the hit to the brick grid and break the brick 45 | balls[b].v[d] = -balls[b].v[d]; // bounce the ball off the brick 46 | balls[b].x[d] += balls[b].v[d] > 0 ? 1 : -1; 47 | } 48 | } 49 | 50 | for (int b=0; b<2; b++) // imprint the balls into the battlefield 51 | xor_bricks(balls[b].x[0], balls[b].x[1], !b); 52 | for (int j=0; j<48; j+=2) { // show the battlefield 53 | for (int i=0; i<48; i++) { 54 | printf("\033[48;2;%sm",palette[bricks[i + (j+0)*48]]); // set background color 55 | printf("\033[38;2;%sm",palette[bricks[i + (j+1)*48]]); // set foreground color 56 | printf("\xE2\x96\x83"); // half-block Unicode symbol 57 | } 58 | printf("\033[49m\n"); 59 | } 60 | for (int b=0; b<2; b++) // clear the balls from the battlefield 61 | xor_bricks(balls[b].x[0], balls[b].x[1], !b); 62 | usleep(1000000/50); // fps 63 | } 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /test-programs/gfx/fire.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define WIDTH 80 7 | #define HEIGHT 25 8 | #define FPS 30 9 | 10 | const char* palette[256] = { 11 | #define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m " 12 | ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36), 13 | ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16), 14 | ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4), 15 | ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0), 16 | ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0), 17 | ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0), 18 | ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0), 19 | ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0), 20 | ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0), 21 | ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0), 22 | ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0), 23 | ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0), 24 | ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0), 25 | ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0), 26 | ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0), 27 | ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0), 28 | #define W ANSIRGB(252,252,252) 29 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 30 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 31 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 32 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 33 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 34 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 35 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 36 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W 37 | #undef W 38 | #undef ANSIRGB 39 | }; 40 | 41 | static uint8_t fire[WIDTH * HEIGHT]; 42 | 43 | void line_blur(int offset, int step, int nsteps) { 44 | uint8_t circ[3] = {0, fire[offset], fire[offset+step]}; 45 | uint8_t beg = 1; 46 | for (int i=0; i0) 72 | fire[i]--; 73 | 74 | for (int i = 0; i 2 | #include 3 | #include 4 | #include 5 | 6 | #define WIDTH 80 7 | #define HEIGHT 25 8 | #define FPS 30 9 | 10 | const char* palette[256] = { 11 | #define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m " 12 | ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36), 13 | ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16), 14 | ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4), 15 | ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0), 16 | ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0), 17 | ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0), 18 | ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0), 19 | ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0), 20 | ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0), 21 | ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0), 22 | ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0), 23 | ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0), 24 | ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0), 25 | ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0), 26 | ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0), 27 | ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0), 28 | #define W ANSIRGB(252,252,252) 29 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 30 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 31 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 32 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 33 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 34 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 35 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 36 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W 37 | #undef W 38 | #undef ANSIRGB 39 | }; 40 | 41 | #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) 42 | #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) 43 | #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) 44 | #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) 45 | #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) 46 | #define EVAL5(...) __VA_ARGS__ 47 | 48 | #define CONCAT(a,b) a##b 49 | 50 | #define IF_ELSE(b) CONCAT(IF_,b) 51 | #define IF_0(...) ELSE_0 52 | #define IF_1(...) __VA_ARGS__ ELSE_1 53 | #define ELSE_0(...) __VA_ARGS__ 54 | #define ELSE_1(...) 55 | 56 | #define SECOND(a, b, ...) b 57 | #define TEST(...) SECOND(__VA_ARGS__, 0) 58 | #define ISZERO(n) TEST(ISZERO_ ## n) 59 | #define ISZERO_0 ~, 1 60 | 61 | #define DEC(n) CONCAT(DEC_, n) 62 | #define DEC_0 0 63 | #define DEC_1 0 64 | #define DEC_2 1 65 | #define DEC_3 2 66 | #define DEC_4 3 67 | #define DEC_5 4 68 | #define DEC_6 5 69 | #define DEC_7 6 70 | #define DEC_8 7 71 | #define DEC_9 8 72 | #define DEC_10 9 73 | #define DEC_11 10 74 | 75 | #define EMPTY() 76 | #define DELAY(id) id EMPTY() 77 | #define DELAY2(...) __VA_ARGS__ DELAY(EMPTY)() 78 | 79 | #define DEPTH 11 80 | #define FIRE(d,id) \ 81 | IF_ELSE(ISZERO(d)) \ 82 | ( uint8_t id; ) \ 83 | ( \ 84 | DELAY2(FIRE_)( DEC(d), id##0 ) \ 85 | DELAY2(FIRE_)( DEC(d), id##1 ) \ 86 | ) 87 | #define FIRE_(...) DELAY(FIRE)(__VA_ARGS__) 88 | EVAL(FIRE(DEPTH,fire)) 89 | 90 | uint8_t get_fire(int i) { 91 | #define GETTER(d,m,id) \ 92 | IF_ELSE(ISZERO(d)) \ 93 | ( return id; ) \ 94 | ( \ 95 | if (i<(m)) \ 96 | DELAY2(GETTER_)(DEC(d), ((m)-(1<0) 145 | set_fire(i, get_fire(i)-1); 146 | 147 | for (int i = 0; i 2 | #include 3 | #include 4 | 5 | int main() { 6 | char c[] = ".,'~=+:;[/<&?oxOX# "; 7 | int32_t l = strlen(c); 8 | 9 | int32_t w = 80; 10 | int32_t h = 25; 11 | int32_t s = 1<<13; 12 | 13 | for (int32_t y=0; y 2 | #include 3 | #include 4 | 5 | #define FPS 24 6 | #define WIDTH 80 7 | #define HEIGHT 50 8 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 9 | 10 | float iTime = 0; 11 | 12 | void mainImage(int fragCoord_x, int fragCoord_y) { // kinda shadertoy naming :) 13 | float u = (2.*fragCoord_x - WIDTH )/HEIGHT; 14 | float v = (2.*fragCoord_y - HEIGHT)/HEIGHT; 15 | float d1 = .6/sqrt(pow(sin(iTime*.5) -u, 2) + pow(sin(iTime*.5)-v, 2)); // linear motion 16 | float d2 = .6/sqrt(pow(sin(iTime*.5) -u, 2) + pow(cos(iTime*.5)-v, 2)); // circular motion 17 | float d3 = .6/sqrt(pow(sin(iTime*.25)-u, 2) + pow(sin(iTime) -v, 2)); // wave 18 | float sdf = d1 + d2 + d3 - 2.2; // metaballs signed distance function 19 | float fragColor_r = 255*1.7*(sdf+1); // orange halo (red and green channels) 20 | float fragColor_g = 255*0.8*(sdf+1); 21 | float fragColor_b = 255*(sdf<0 ? 0 : 1); // cold-white metaballs (step function) 22 | printf("%d;%d;%d", CLAMP((int)fragColor_r,0,255), CLAMP((int)fragColor_g, 0, 255), CLAMP((int)fragColor_b, 0, 255)); 23 | } 24 | 25 | int main() { 26 | printf("\033[2J\033[?25l"); // clear screen and hide cursor 27 | for (;;) { 28 | printf("\033[H"); // home 29 | for (int j = 0; j0 { sign = 1; } else { sign = -1; x = -x; } // 13 | while x>+79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 14 | if x>26353589 { return sign*cos24(x - 26353589); } // 15 | return sign*(x/4096)*((16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/131 - 2785856)/4096))/4096); 16 | } 17 | 18 | // cos(x) = 1 + x^2 * (0.03705 * x^2 - 0.49670)) 19 | // this formula works pretty well in the range [-pi/2, +pi/2] 20 | int cos24(int x) { 21 | if x<0 { x = -x; } // 22 | while x>79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 23 | if x>26353589 { return -sin24(x - 26353589); } // 24 | return 16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/27 - 8333243)/4096); 25 | } 26 | 27 | // square root of a fixed-point number 28 | // stored in a 32 bit integer variable, shift is the precision 29 | int sqrt(int n, int shift) { 30 | int x; 31 | int x_old; 32 | int n_one; 33 | 34 | int abs(int x) { 35 | if x < 0 { 36 | return -x; 37 | } else { 38 | return x; 39 | } 40 | } 41 | 42 | if n > 2147483647/shift { // pay attention to potential overflows 43 | return 2 * sqrt(n / 4, shift); 44 | } 45 | x = shift; // initial guess 1.0, can do better, but oh well 46 | n_one = n * shift; // need to compensate for fixp division 47 | while true { 48 | x_old = x; 49 | x = (x + n_one / x) / 2; 50 | if abs(x - x_old) <= 1 { 51 | return x; 52 | } 53 | } 54 | } 55 | 56 | mainImage(int fragCoord_x, int fragCoord_y) { // kinda shadertoy naming :) 57 | int u; int v; 58 | int fragColor_r; int fragColor_g; int fragColor_b; 59 | int sdf; 60 | int a; int b; int c; int d; int e; 61 | int d1; int d2; int d3; 62 | 63 | int CLAMP(int x, int low, int high) { 64 | if x>high { 65 | return high; 66 | } 67 | if xPOW2_24*100 { // 100 approx 32 pi :) 118 | iTime = 0; 119 | } 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /test-programs/gfx/race.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define FPS 24 6 | #define MULTI 2 7 | #define WIDTH (80*MULTI) 8 | #define HEIGHT (50*MULTI) 9 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 10 | float iTime = 0; 11 | 12 | void mainImage(int fragCoord_x, int fragCoord_y, int *fragColor_r, int *fragColor_g, int *fragColor_b) { // kinda shadertoy naming :) 13 | float u = (fragCoord_x - WIDTH/2.)/HEIGHT; 14 | float v = -(fragCoord_y - HEIGHT/2.)/HEIGHT; 15 | float horizon = .32; 16 | float persp = 1./((horizon+.05)-v); 17 | float t = sin(iTime/4.); 18 | float t3 = t*t*t; // cubic law allows for longer straight roads 19 | float x = pow(t3 + u*persp - .05*t3*persp*persp, 2.); 20 | float y = 2.*persp+20.*iTime; // x,y are texture coordinates 21 | int dash = sin(y)>0; // a bit overkill to use sine function here, simple mod/fmod would do well 22 | 23 | if (v>horizon) { 24 | *fragColor_r = 255*(1.-v); // sunset sky 25 | *fragColor_g = 128; 26 | *fragColor_b = 178; 27 | } else { // actual texture to map 28 | *fragColor_r = *fragColor_g = *fragColor_b = 128; // asphalt 29 | if (x>4.) { // grass 30 | *fragColor_r = 0; 31 | *fragColor_g = 178; 32 | *fragColor_b = 0; 33 | } else if (x>2.) { // red-white curb 34 | *fragColor_r = 255; 35 | *fragColor_g = dash ? 255 : 0; 36 | *fragColor_b = dash ? 255 : 0; 37 | } else if (x<.015 && !dash) // central line 38 | *fragColor_r = *fragColor_g = *fragColor_b = 255; 39 | *fragColor_r = CLAMP(*fragColor_r*(1-v), 0, 255); // headlights 40 | *fragColor_g = CLAMP(*fragColor_g*(1-v), 0, 255); 41 | *fragColor_b = CLAMP(*fragColor_b*(1-v), 0, 255); 42 | } 43 | } 44 | 45 | void multisample(int fragCoord_x, int fragCoord_y) { 46 | int fragColor_r = 0, fragColor_g = 0, fragColor_b = 0; 47 | int r, g, b; 48 | for (int i=0; i0) { sign = 1; } else { sign = -1; x = -x; } // 13 | while (x>79060768) { x = x - 105414357; } // reduce the argument to the acceptable range 14 | if (x>26353589) { return sign*sin24(52707179 - x); } // 15 | return sign*(x/4096)*((16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/131 - 2785856)/4096))/4096); 16 | } 17 | 18 | int mul24(int a, int b) { 19 | return (a/4096)*(b/4096); 20 | } 21 | 22 | int div24(int a, int b) { 23 | return (a/(b/4096))*4096; 24 | } 25 | 26 | mainImage(int fragCoord_x, int fragCoord_y) { 27 | int u; int v; 28 | int horizon; 29 | int persp; 30 | 31 | int t; int t3; 32 | int x; int y; 33 | bool band; 34 | 35 | int CLAMP(int x, int low, int high) { 36 | if x>high { 37 | return high; 38 | } 39 | if xhorizon) { 51 | fragColor_r = (255*((POW2_24-v)/4096))/4096; // sky 52 | fragColor_g = 128; 53 | fragColor_b = 178; 54 | } else { 55 | persp = div24(POW2_24, horizon + POW2_24/13 - v); 56 | t = sin24(iTime/4); 57 | t3 = mul24(mul24(t, t), t); 58 | x = t3 + mul24(u, persp) - mul24(mul24(t3/10, persp), persp); 59 | if (x>10*POW2_24 || x<-10*POW2_24) { // ugly hack to avoid overflow: if x is large, it is grass 60 | fragColor_r = 0; 61 | fragColor_g = 178; 62 | fragColor_b = 0; 63 | } else { 64 | x = mul24(x, x); 65 | y = 2*persp+ 20*iTime; 66 | band = sin24(y)>0; 67 | if (x>4*POW2_24) { // grass 68 | fragColor_r = 0; 69 | fragColor_g = 178; 70 | fragColor_b = 0; 71 | } else { 72 | if (x>2*POW2_24) { // red-white curb 73 | if (band) { 74 | fragColor_r = 255; 75 | fragColor_g = 255; 76 | fragColor_b = 255; 77 | } else { 78 | fragColor_r = 255; 79 | fragColor_g = 0; 80 | fragColor_b = 0; 81 | } 82 | } else { // road 83 | fragColor_r = 128; 84 | fragColor_g = 128; 85 | fragColor_b = 128; // asphalt 86 | if (xPOW2_24*100 { // 100 approx 32 pi :) 144 | iTime = 0; 145 | } 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /test-programs/nontrivial/bitwise.expected: -------------------------------------------------------------------------------- 1 | bitwise and -1804289383 1681692777 1957747793 -719885386 596516649 1025202362 783368690 -2044897763 2 | -1804289383 -1804289383 70555657 338728977 -1810617712 268809 336599192 70254736 -2079059431 3 | 1681692777 70555657 1681692777 1680906305 1142163488 537663529 605558824 607125600 68947977 4 | 1957747793 338728977 1680906305 1957747793 1410353168 545266689 873486352 615530576 68178961 5 | -719885386 -1810617712 1142163488 1410353168 -719885386 17173280 353585330 68239794 -2078981612 6 | 596516649 268809 537663529 545266689 17173280 596516649 554309672 578814240 34346505 7 | 1025202362 336599192 605558824 873486352 353585330 554309672 1025202362 739328178 68767768 8 | 783368690 70254736 607125600 615530576 68239794 578814240 739328178 783368690 101793808 9 | -2044897763 -2079059431 68947977 68178961 -2078981612 34346505 68767768 101793808 -2044897763 10 | 11 | bitwise or -1804289383 1681692777 1957747793 -719885386 596516649 1025202362 783368690 -2044897763 12 | -1804289383 -1804289383 -193152263 -185270567 -713557057 -1208041543 -1115686213 -1091175429 -1770127715 13 | 1681692777 -193152263 1681692777 1958534265 -180356097 1740545897 2101336315 1857935867 -432152963 14 | 1957747793 -185270567 1958534265 1957747793 -172490761 2008997753 2109463803 2125585907 -155328931 15 | -719885386 -713557057 -180356097 -172490761 -719885386 -140542017 -48268354 -4756490 -685801537 16 | 596516649 -1208041543 1740545897 2008997753 -140542017 596516649 1067409339 801071099 -1482727619 17 | 1025202362 -1115686213 2101336315 2109463803 -48268354 1067409339 1025202362 1069242874 -1088463169 18 | 783368690 -1091175429 1857935867 2125585907 -4756490 801071099 1069242874 783368690 -1363322881 19 | -2044897763 -1770127715 -432152963 -155328931 -685801537 -1482727619 -1088463169 -1363322881 -2044897763 20 | 21 | bitwise xor -1804289383 1681692777 1957747793 -719885386 596516649 1025202362 783368690 -2044897763 22 | -1804289383 0 -263707920 -523999544 1097060655 -1208310352 -1452285405 -1161430165 308931716 23 | 1681692777 -263707920 0 277627960 -1322519585 1202882368 1495777491 1250810267 -501100940 24 | 1957747793 -523999544 277627960 0 -1582843929 1463731064 1235977451 1510055331 -223507892 25 | -719885386 1097060655 -1322519585 -1582843929 0 -157715297 -401853684 -72996284 1393180075 26 | 596516649 -1208310352 1202882368 1463731064 -157715297 0 513099667 222256859 -1517074124 27 | 1025202362 -1452285405 1495777491 1235977451 -401853684 513099667 0 329914696 -1157230937 28 | 783368690 -1161430165 1250810267 1510055331 -72996284 222256859 329914696 0 -1465116689 29 | -2044897763 308931716 -501100940 -223507892 1393180075 -1517074124 -1157230937 -1465116689 0 30 | 31 | -1804289383 1681692777 1957747793 -719885386 596516649 1025202362 783368690 -2044897763 32 | bitwise not 1804289382 -1681692778 -1957747794 719885385 -596516650 -1025202363 -783368691 2044897762 33 | -------------------------------------------------------------------------------- /test-programs/nontrivial/bitwise.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int i; int j; 3 | 4 | // _ _ _____ 5 | // /\ | \ | | __ \ 6 | // / \ | \| | | | | 7 | // / /\ \ | . ` | | | | 8 | // / ____ \| |\ | |__| | 9 | // /_/ \_\_| \_|_____/ 10 | 11 | int and(int a, int b) { 12 | int result; 13 | int pow; 14 | 15 | result = 0; 16 | if (a<0 && b<0) { 17 | result = -2147483648; 18 | } 19 | if (a<0) { 20 | a = a + 2147483648; 21 | } 22 | if (b<0) { 23 | b = b + 2147483648; 24 | } 25 | pow = 1; 26 | while a>0 || b>0 { 27 | if a % 2 == 1 && b % 2 == 1 { 28 | result = result + pow; 29 | } 30 | a = a / 2; 31 | b = b / 2; 32 | pow = pow * 2; 33 | } 34 | return result; 35 | } 36 | 37 | // __ ______ _____ 38 | // \ \ / / __ \| __ \ 39 | // \ V / | | | |__) | 40 | // > <| | | | _ / 41 | // / . \ |__| | | \ \ 42 | // /_/ \_\____/|_| \_\ 43 | 44 | int xor(int a, int b) { 45 | return a - and(a,b) + b - and(a,b); 46 | } 47 | 48 | // ____ _____ 49 | // / __ \| __ \ 50 | // | | | | |__) | 51 | // | | | | _ / 52 | // | |__| | | \ \ 53 | // \____/|_| \_\ 54 | 55 | int or(int a, int b) { 56 | return xor(xor(a,b),and(a,b)); 57 | } 58 | 59 | // _ _ ____ _______ 60 | // | \ | |/ __ \__ __| 61 | // | \| | | | | | | 62 | // | . ` | | | | | | 63 | // | |\ | |__| | | | 64 | // |_| \_|\____/ |_| 65 | 66 | int not(int a) { 67 | return -1 - a; 68 | } 69 | 70 | // _ _ _ _ 71 | // | | | | (_) | | 72 | // _ __ _ __ ___| |_| |_ _ _ _ __ _ __ _ _ __ | |_ 73 | // | '_ \| '__/ _ \ __| __| | | | | '_ \| '__| | '_ \| __| 74 | // | |_) | | | __/ |_| |_| |_| | | |_) | | | | | | | |_ 75 | // | .__/|_| \___|\__|\__|\__, | | .__/|_| |_|_| |_|\__| 76 | // | | __/ | | | 77 | // |_| |___/ |_| 78 | 79 | padprint(int a) { 80 | int n; 81 | int nspaces; 82 | 83 | n = a; 84 | nspaces = 16; 85 | if (n<=0) { 86 | nspaces = nspaces - 1; 87 | } 88 | while n!=0 { 89 | n = n / 10; 90 | nspaces = nspaces - 1; 91 | } 92 | n = 0; 93 | while n _ < | __/ _ \/ __| __| / __/ _` / __|/ _ \/ __| 104 | // | (_) | | || __/\__ \ |_ | (_| (_| \__ \ __/\__ \ 105 | // \___/ \__\___||___/\__| \___\__,_|___/\___||___/ 106 | 107 | int test(int i) { 108 | if (i<4) { 109 | if (i<2) { 110 | if (i<1) { 111 | return -1804289383; 112 | } else { 113 | return 1681692777; 114 | } 115 | } else { 116 | if (i<3) { 117 | return 1957747793; 118 | } else { 119 | return -719885386; 120 | } 121 | } 122 | } else { 123 | if (i<6) { 124 | if (i<5) { 125 | return 596516649; 126 | } else { 127 | return 1025202362; 128 | } 129 | } else { 130 | if (i<7) { 131 | return 783368690; 132 | } else { 133 | return -2044897763; 134 | } 135 | } 136 | } 137 | } 138 | 139 | // _ _ _ _ _ 140 | // | | | | | | | | 141 | // _ __ _ _ _ __ | |_ ___ ___| |_ ___| | | | 142 | // | '__| | | | '_ \ | __/ _ \/ __| __/ __| | | | 143 | // | | | |_| | | | | | || __/\__ \ |_\__ \_|_|_| 144 | // |_| \__,_|_| |_| \__\___||___/\__|___(_|_|_) 145 | 146 | // _ _ _____ 147 | // /\ | \ | | __ \ 148 | // / \ | \| | | | | 149 | // / /\ \ | . ` | | | | 150 | // / ____ \| |\ | |__| | 151 | // /_/ \_\_| \_|_____/ 152 | 153 | print " bitwise and"; 154 | i = 0; 155 | while i<8 { 156 | padprint(test(i)); 157 | i = i + 1; 158 | } 159 | print "\n"; 160 | i = 0; 161 | while i<8 { 162 | padprint(test(i)); 163 | j = 0; 164 | while j<8 { 165 | padprint(and(test(i), test(j))); 166 | j = j + 1; 167 | } 168 | print "\n"; 169 | i = i + 1; 170 | } 171 | print "\n"; 172 | 173 | // ____ _____ 174 | // / __ \| __ \ 175 | // | | | | |__) | 176 | // | | | | _ / 177 | // | |__| | | \ \ 178 | // \____/|_| \_\ 179 | 180 | print " bitwise or"; 181 | i = 0; 182 | while i<8 { 183 | padprint(test(i)); 184 | i = i + 1; 185 | } 186 | print "\n"; 187 | i = 0; 188 | while i<8 { 189 | padprint(test(i)); 190 | j = 0; 191 | while j<8 { 192 | padprint(or(test(i), test(j))); 193 | j = j + 1; 194 | } 195 | print "\n"; 196 | i = i + 1; 197 | } 198 | print "\n"; 199 | 200 | // __ ______ _____ 201 | // \ \ / / __ \| __ \ 202 | // \ V / | | | |__) | 203 | // > <| | | | _ / 204 | // / . \ |__| | | \ \ 205 | // /_/ \_\____/|_| \_\ 206 | 207 | print " bitwise xor"; 208 | i = 0; 209 | while i<8 { 210 | padprint(test(i)); 211 | i = i + 1; 212 | } 213 | print "\n"; 214 | i = 0; 215 | while i<8 { 216 | padprint(test(i)); 217 | j = 0; 218 | while j<8 { 219 | padprint(xor(test(i), test(j))); 220 | j = j + 1; 221 | } 222 | print "\n"; 223 | i = i + 1; 224 | } 225 | print "\n"; 226 | 227 | // _ _ ____ _______ 228 | // | \ | |/ __ \__ __| 229 | // | \| | | | | | | 230 | // | . ` | | | | | | 231 | // | |\ | |__| | | | 232 | // |_| \_|\____/ |_| 233 | 234 | print " "; 235 | i = 0; 236 | while i<8 { 237 | padprint(test(i)); 238 | i = i + 1; 239 | } 240 | print "\n bitwise not"; 241 | j = 0; 242 | while j<8 { 243 | padprint(not(test(j))); 244 | j = j + 1; 245 | } 246 | print "\n"; 247 | } 248 | 249 | -------------------------------------------------------------------------------- /test-programs/nontrivial/mandelbrot.expected: -------------------------------------------------------------------------------- 1 | ...............,,,,,,,,,,,,,,,,,,''''''''''''''''''''''''''''''',,,,,,,,,,,,,,,, 2 | ............,,,,,,,,,,,,''''''''''''''''''''''''''''~~~~~~==~~~~~'''''''',,,,,,, 3 | ..........,,,,,,,,,''''''''''''''''''''''''''~~~~~~~~~==++<&:+++=~~~~~~''''''',, 4 | ........,,,,,,,'''''''''''''''''''''''''~~~~~~~~~~~====+::;/&O/[:+==~~~~~~~''''' 5 | .......,,,,'''''''''''''''''''''''''~~~~~~~~~~~~====++:? x O=18) { print " "; } 38 | } 39 | 40 | l = 19; 41 | w = 80; 42 | h = 25; 43 | s = 8192; 44 | 45 | y = 0; 46 | while (y 2147483647/shift { // pay attention to potential overflows 11 | return 2 * sqrt(n / 4, shift); 12 | } 13 | x = shift; // initial guess 1.0, can do better, but oh well 14 | n_one = n * shift; // need to compensate for fixp division 15 | while true { 16 | x_old = x; 17 | x = (x + n_one / x) / 2; 18 | if abs(x - x_old) <= 1 { 19 | return x; 20 | } 21 | } 22 | } 23 | 24 | int abs(int x) { 25 | if x < 0 { 26 | return -x; 27 | } else { 28 | return x; 29 | } 30 | } 31 | 32 | // 25735 is approximately equal to pi * 8192; 33 | // expected value of the output is sqrt(pi) * 8192 approx 14519 34 | 35 | println sqrt(25735, 8192); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /test-programs/nontrivial/trig-hp12c.expected: -------------------------------------------------------------------------------- 1 | pi/2 approx 1.57079631 2 | sin(pi/2) approx 1.00000286 3 | cos(pi/2) approx 0.00037169 4 | -------------------------------------------------------------------------------- /test-programs/nontrivial/trig-hp12c.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int PI_2; // pi/2 in fixed point precision 3 | int pow2_24; // 2^24 precision 4 | 5 | print_fixed_point(int x, int shift) { 6 | int decpr; 7 | int i; 8 | 9 | print x / shift; 10 | print "."; 11 | 12 | decpr = 0; // number of meaningful digits after the decimal point 13 | i = shift; 14 | while i>0 { 15 | decpr = decpr + 1; 16 | i = i / 10; 17 | } 18 | 19 | i = 0; 20 | x = (x % shift) * 10; 21 | while i0 { sign = 1; } else { sign = -1; x = -x; } // 34 | while x>+79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 35 | if x>26353589 { return sign*sin24(52707179 - x); } // 36 | return sign*(x/4096)*((16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/131 - 2785856)/4096))/4096); 37 | } 38 | 39 | // cos(x) = 1 + x^2 * (0.03705 * x^2 - 0.49670)) 40 | // this formula works pretty well in the range [-pi/2, +pi/2] 41 | int cos24(int x) { 42 | if x<0 { x = -x; } // 43 | while x>79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 44 | if x>26353589 { return -sin24(x - 26353589); } // 45 | return 16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/27 - 8333243)/4096); 46 | } 47 | 48 | PI_2 = 26353589; 49 | pow2_24 = 16777216; 50 | print "pi/2 approx "; print_fixed_point(PI_2, pow2_24); 51 | print "sin(pi/2) approx "; print_fixed_point(sin24(PI_2), pow2_24); 52 | print "cos(pi/2) approx "; print_fixed_point(cos24(PI_2), pow2_24); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /test-programs/simple/collatz.expected: -------------------------------------------------------------------------------- 1 | 111 2 | -------------------------------------------------------------------------------- /test-programs/simple/collatz.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int collatz(int n) { 3 | int its; 4 | 5 | its = 0; 6 | while (n != 1) { 7 | its = its + 1; 8 | if (n % 2 == 0) { 9 | n = n / 2; 10 | } else { 11 | n = 3*n + 1; 12 | } 13 | } 14 | return its; 15 | } 16 | println collatz(27); 17 | } 18 | -------------------------------------------------------------------------------- /test-programs/simple/eight-queens.expected: -------------------------------------------------------------------------------- 1 | Q....... 2 | ......Q. 3 | ....Q... 4 | .......Q 5 | .Q...... 6 | ...Q.... 7 | .....Q.. 8 | ..Q..... 9 | -------------------------------------------------------------------------------- /test-programs/simple/eight-queens.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | bool board0; bool board1; bool board2; bool board3; bool board4; bool board5; bool board6; bool board7; bool board8; bool board9; bool board10; bool board11; bool board12; bool board13; bool board14; bool board15; bool board16; bool board17; bool board18; bool board19; bool board20; bool board21; bool board22; bool board23; bool board24; bool board25; bool board26; bool board27; bool board28; bool board29; bool board30; bool board31; bool board32; bool board33; bool board34; bool board35; bool board36; bool board37; bool board38; bool board39; bool board40; bool board41; bool board42; bool board43; bool board44; bool board45; bool board46; bool board47; bool board48; bool board49; bool board50; bool board51; bool board52; bool board53; bool board54; bool board55; bool board56; bool board57; bool board58; bool board59; bool board60; bool board61; bool board62; bool board63; 3 | int i; int j; 4 | 5 | // getter-setter for the board 6 | bool board(int i) { 7 | if i < 32 { if i < 16 { if i < 8 { if i < 4 { if i < 2 { if i < 1 { return board0; } else { return board1; }} else { if i < 3 { return board2; } else { return board3; }}} else { if i < 6 { if i < 5 { return board4; } else { return board5; }} else { if i < 7 { return board6; } else { return board7; }}}} else { if i < 12 { if i < 10 { if i < 9 { return board8; } else { return board9; }} else { if i < 11 { return board10; } else { return board11; }}} else { if i < 14 { if i < 13 { return board12; } else { return board13; }} else { if i < 15 { return board14; } else { return board15; }}}}} else { if i < 24 { if i < 20 { if i < 18 { if i < 17 { return board16; } else { return board17; }} else { if i < 19 { return board18; } else { return board19; }}} else { if i < 22 { if i < 21 { return board20; } else { return board21; }} else { if i < 23 { return board22; } else { return board23; }}}} else { if i < 28 { if i < 26 { if i < 25 { return board24; } else { return board25; }} else { if i < 27 { return board26; } else { return board27; }}} else { if i < 30 { if i < 29 { return board28; } else { return board29; }} else { if i < 31 { return board30; } else { return board31; }}}}}} else { if i < 48 { if i < 40 { if i < 36 { if i < 34 { if i < 33 { return board32; } else { return board33; }} else { if i < 35 { return board34; } else { return board35; }}} else { if i < 38 { if i < 37 { return board36; } else { return board37; }} else { if i < 39 { return board38; } else { return board39; }}}} else { if i < 44 { if i < 42 { if i < 41 { return board40; } else { return board41; }} else { if i < 43 { return board42; } else { return board43; }}} else { if i < 46 { if i < 45 { return board44; } else { return board45; }} else { if i < 47 { return board46; } else { return board47; }}}}} else { if i < 56 { if i < 52 { if i < 50 { if i < 49 { return board48; } else { return board49; }} else { if i < 51 { return board50; } else { return board51; }}} else { if i < 54 { if i < 53 { return board52; } else { return board53; }} else { if i < 55 { return board54; } else { return board55; }}}} else { if i < 60 { if i < 58 { if i < 57 { return board56; } else { return board57; }} else { if i < 59 { return board58; } else { return board59; }}} else { if i < 62 { if i < 61 { return board60; } else { return board61; }} else { if i < 63 { return board62; } else { return board63; }}}}}} 8 | } 9 | 10 | board(int i, bool v) { 11 | if i < 32 { if i < 16 { if i < 8 { if i < 4 { if i < 2 { if i < 1 { board0 = v; } else { board1 = v; }} else { if i < 3 { board2 = v; } else { board3 = v; }}} else { if i < 6 { if i < 5 { board4 = v; } else { board5 = v; }} else { if i < 7 { board6 = v; } else { board7 = v; }}}} else { if i < 12 { if i < 10 { if i < 9 { board8 = v; } else { board9 = v; }} else { if i < 11 { board10 = v; } else { board11 = v; }}} else { if i < 14 { if i < 13 { board12 = v; } else { board13 = v; }} else { if i < 15 { board14 = v; } else { board15 = v; }}}}} else { if i < 24 { if i < 20 { if i < 18 { if i < 17 { board16 = v; } else { board17 = v; }} else { if i < 19 { board18 = v; } else { board19 = v; }}} else { if i < 22 { if i < 21 { board20 = v; } else { board21 = v; }} else { if i < 23 { board22 = v; } else { board23 = v; }}}} else { if i < 28 { if i < 26 { if i < 25 { board24 = v; } else { board25 = v; }} else { if i < 27 { board26 = v; } else { board27 = v; }}} else { if i < 30 { if i < 29 { board28 = v; } else { board29 = v; }} else { if i < 31 { board30 = v; } else { board31 = v; }}}}}} else { if i < 48 { if i < 40 { if i < 36 { if i < 34 { if i < 33 { board32 = v; } else { board33 = v; }} else { if i < 35 { board34 = v; } else { board35 = v; }}} else { if i < 38 { if i < 37 { board36 = v; } else { board37 = v; }} else { if i < 39 { board38 = v; } else { board39 = v; }}}} else { if i < 44 { if i < 42 { if i < 41 { board40 = v; } else { board41 = v; }} else { if i < 43 { board42 = v; } else { board43 = v; }}} else { if i < 46 { if i < 45 { board44 = v; } else { board45 = v; }} else { if i < 47 { board46 = v; } else { board47 = v; }}}}} else { if i < 56 { if i < 52 { if i < 50 { if i < 49 { board48 = v; } else { board49 = v; }} else { if i < 51 { board50 = v; } else { board51 = v; }}} else { if i < 54 { if i < 53 { board52 = v; } else { board53 = v; }} else { if i < 55 { board54 = v; } else { board55 = v; }}}} else { if i < 60 { if i < 58 { if i < 57 { board56 = v; } else { board57 = v; }} else { if i < 59 { board58 = v; } else { board59 = v; }}} else { if i < 62 { if i < 61 { board60 = v; } else { board61 = v; }} else { if i < 63 { board62 = v; } else { board63 = v; }}}}}} 12 | } 13 | 14 | bool valid_position(int row, int col) { // Is it safe to plase a queen in this position? 15 | int r; int c; // The board is filled left-to-right (it contains col-1 queens), 16 | c = 0; // so we need to check to the left of row,col position. 17 | while c=0 && r>=0 { 25 | if board(c+r*8) { // Check the diagonal. 26 | return false; 27 | } 28 | r = r - 1; 29 | c = c - 1; 30 | } 31 | r = row; c = col; 32 | while r<8 && c>=0 { 33 | if board(c+r*8) { // Check the anti-diagonal. 34 | return false; 35 | } 36 | r = r + 1; 37 | c = c - 1; 38 | } 39 | return true; 40 | } 41 | 42 | bool solve(int col) { // Place a queen on the first column, 43 | int row; // then proceed to the next column and place 44 | if col == 8 { // a queen in the first safe row of that column. 45 | return true; // Rinse, repeat. 46 | } 47 | row = 0; 48 | while row<8 { 49 | if valid_position(row, col) { 50 | board(col+row*8, true); 51 | if solve(col+1) { 52 | return true; 53 | } 54 | board(col+row*8, false); 55 | } 56 | row = row + 1; 57 | } 58 | return false; 59 | } 60 | 61 | i = 0; 62 | while i<64 { 63 | board(i, false); 64 | i = i + 1; 65 | } 66 | solve(0); 67 | j = 0; 68 | while j<8 { 69 | i = 0; 70 | while i<8 { 71 | if board(i+j*8) { 72 | print "Q"; 73 | } else { 74 | print "."; 75 | } 76 | i = i + 1; 77 | } 78 | println ""; 79 | j = j + 1; 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /test-programs/simple/fixed-point.expected: -------------------------------------------------------------------------------- 1 | -3.14158 2 | -------------------------------------------------------------------------------- /test-programs/simple/fixed-point.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | 3 | // This program outputs a decimal representation of a signed 32-bit fixed point number. 4 | // It prints the number one character at a time without ever printing an integer type expression, 5 | // like we would make it with aid of _write system call. 6 | 7 | print_fixed_point(int x, int shift) { 8 | int decpr; 9 | int i; 10 | 11 | int decpr(int shift) { // number of meaningful digits after the decimal point 12 | int decpr; 13 | decpr = 0; 14 | while shift>0 { 15 | decpr = decpr + 1; 16 | shift = shift / 10; 17 | } 18 | return decpr; 19 | } 20 | 21 | print_digit(int d) { 22 | if d==0 { 23 | print "0"; 24 | } else { 25 | if d==1 { 26 | print "1"; 27 | } else { 28 | if d==2 { 29 | print "2"; 30 | } else { 31 | if d==3 { 32 | print "3"; 33 | } else { 34 | if d==4 { 35 | print "4"; 36 | } else { 37 | if d==5 { 38 | print "5"; 39 | } else { 40 | if d==6 { 41 | print "6"; 42 | } else { 43 | if d==7 { 44 | print "7"; 45 | } else { 46 | if d==8 { 47 | print "8"; 48 | } else { 49 | if d==9 { 50 | print "9"; 51 | } else { 52 | print "invalid input"; 53 | }}}}}}}}}} 54 | } 55 | 56 | print_integer(int x) { 57 | if x>0 { 58 | print_integer(x / 10); 59 | print_digit(x % 10); // recursion to reverse the print 60 | } 61 | } 62 | 63 | if x<0 { 64 | print "-"; 65 | x = -x; 66 | } 67 | 68 | print_integer(x / shift); 69 | print "."; 70 | 71 | decpr = decpr(shift); 72 | i = 0; 73 | x = (x % shift) * 10; 74 | while i