Skip to content

Commit fac8b8c

Browse files
committed
Add variadic macro and function definition support. Add a C example
Add non-constant array indexing, and some other fixes too
1 parent 7c2c92d commit fac8b8c

File tree

3 files changed

+193
-26
lines changed

3 files changed

+193
-26
lines changed

compiler/compiler.py

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,20 @@ def get_output(self):
6464

6565
class ParamStr(str):
6666

67+
def __new__(cls, s, args, n):
68+
self = super().__new__(cls, s)
69+
self._args = args
70+
self._arg_idx = n
71+
return self
72+
6773
@property
6874
def escape(self):
69-
return '"%s"' % self.replace('"', '\\"')
75+
return ParamStr('"%s"' % self.replace('"', '\\"'), self._args,
76+
self._arg_idx)
77+
78+
@property
79+
def remainder(self):
80+
return ParamStr(','.join(self._args[self._arg_idx:]), [], 0)
7081

7182
class Preprocessor:
7283

@@ -119,6 +130,7 @@ def substitute(self, line):
119130
args = []
120131
s = ''
121132
end = start
133+
arg_num = 0
122134
for c in line[start:]:
123135
end += 1
124136
if c == '(':
@@ -129,11 +141,12 @@ def substitute(self, line):
129141
break
130142
if brackets == 0:
131143
if c == ',':
132-
args.append(s)
144+
args.append(ParamStr(s, args, arg_num))
133145
s = ''
146+
arg_num += 1
134147
continue
135148
s += c
136-
args.append(ParamStr(s))
149+
args.append(ParamStr(s, args, arg_num))
137150
line = line[:idx] + replacement.format(*args) + line[end:]
138151
# Recursively substitute
139152
line = self.substitute(line)
@@ -230,7 +243,7 @@ def handle_define(self, arg):
230243
if not self.block.output:
231244
return
232245
import re
233-
match = re.match('(\w+)\s*(\((?:\w+\s*,\s*)*(?:\w+)\s*\))?\s+(.+)', arg)
246+
match = re.match('(\w+)\s*(\((?:\w+\s*,\s*)*(?:(?:\w+|\.\.\.))\s*\))?\s+(.+)', arg)
234247
if not match:
235248
raise Exception('invalid #define')
236249
name = match.group(1)
@@ -242,8 +255,16 @@ def handle_define(self, arg):
242255
else:
243256
params = self._get_params(params)
244257
for i in range(len(params)):
245-
replacement = re.sub(r'(\b|^)#%s(\b|$)' % params[i], '{%d.escape}' % i, replacement)
246-
replacement = re.sub(r'(\b|^)%s(\b|$)' % params[i], '{%d}' % i, replacement)
258+
if params[i] == '...':
259+
replacement = replacement.replace('#__VA_ARGS__',
260+
'{%d.remainder.escape}' % i)
261+
replacement = replacement.replace('__VA_ARGS__',
262+
'{%d.remainder}' % i)
263+
continue
264+
replacement = re.sub(r'(\b|^)#%s(\b|$)' % params[i],
265+
'{%d.escape}' % i, replacement)
266+
replacement = re.sub(r'(\b|^)%s(\b|$)' % params[i],
267+
'{%d}' % i, replacement)
247268

248269
self.replacements[name] = (idx, 'function', replacement)
249270

@@ -521,6 +542,7 @@ def effective(type, ptr, is_array, array_size=None):
521542
Indirect = namedtuple('Indirect', 'ref')
522543
Offset = namedtuple('Offset', 'ref offset')
523544
Direct = namedtuple('Direct', 'ref')
545+
IndirectOffset = namedtuple('IndirectOffset', 'ref offset')
524546

525547
class CompilerVisitor(Visitor):
526548

@@ -578,14 +600,14 @@ def visit_statements(self, stmts):
578600
self.visit_statement(stmt)
579601

580602
def visit_statement(self, stmt):
581-
self.clear_temporary_vars()
582-
583603
if type(stmt) == list:
584604
self.visit_statements(stmt)
585605
return
586606
elif isinstance(stmt, EmptyStatement):
587607
return
588608

609+
self.clear_temporary_vars()
610+
589611
stmt_map = {
590612
Declaration: self.visit_decl,
591613

@@ -634,34 +656,42 @@ def visit_expression(self, expr):
634656
return func(expr)
635657

636658
def visit_func_decl(self, stmt):
659+
self.define_function(stmt.type, stmt.decl, stmt.body)
660+
661+
def define_function(self, type_spec, def_spec, body = [], def_only=False):
637662
# We must not be in a function here
638663
assert self.current_function is None
639-
self.clear_locals()
664+
if not def_only:
665+
self.clear_locals()
640666

641-
assert stmt.type.store is None
642-
assert stmt.type.qual is None
667+
assert type_spec.store is None
668+
assert type_spec.qual is None
643669

644-
ret_type = self.get_effective_type(stmt.type, stmt.decl)
670+
ret_type = self.get_effective_type(type_spec, def_spec)
645671

646-
desc = stmt.decl.name_spec
672+
desc = def_spec.name_spec
647673
assert isinstance(desc, FuncDeclSpec)
648674

649675
name = desc.name.val
650-
self.writer.write_subroutine(name)
676+
if not def_only:
677+
self.writer.write_subroutine(name)
651678

652679
param_types = []
653680
for param in desc.params:
654681
type = self.get_effective_type(param.type, param.decl)
655682
assert param.decl.name_spec is not None
656683
p_name = Types.get_name_for(param.decl.name_spec)
657684
param_types.append(type)
658-
self.add_local(type, p_name)
685+
if not def_only:
686+
self.add_local(type, p_name)
659687

660688
func = Function(ret_type=ret_type, name=name, param_types=param_types)
661-
self.current_function = func
662689
self.functions[name] = func
663-
self.visit_statements(stmt.body)
664-
if not stmt.body or not isinstance(stmt.body[-1], ReturnStmt) \
690+
if def_only:
691+
return
692+
self.current_function = func
693+
self.visit_statements(body)
694+
if not body or not isinstance(body[-1], ReturnStmt) \
665695
and name != 'main': # main function doesn't return
666696
self.write('RET')
667697
self.current_function = None
@@ -685,6 +715,10 @@ def visit_decl(self, decl):
685715
self.visit_type_def(decl)
686716
else:
687717
for init in decl.init:
718+
if isinstance(init.decl.name_spec, FuncDeclSpec):
719+
assert init.val is None
720+
self.define_function(decl.type, init.decl, def_only=True)
721+
continue
688722
type_ = self.get_effective_type(decl.type, init.decl)
689723
name = Types.get_name_for(init.decl.name_spec)
690724
var = self.add_to_scope(type_, name)
@@ -887,10 +921,18 @@ def unwrap(ref, tmp_reg_getter, tmp_reg=None):
887921
tmp_reg = tmp_reg_getter()
888922
actions, b, o = unwrap(ref.ref, tmp_reg_getter, tmp_reg)
889923
return (actions + ((b, o, tmp_reg),), tmp_reg, 0)
924+
if isinstance(ref, IndirectOffset):
925+
if not tmp_reg:
926+
tmp_reg = tmp_reg_getter()
927+
actions, b, o = unwrap(ref.ref, tmp_reg_getter, tmp_reg)
928+
assert type(o) == int, 'cannot indirectly reference this'
929+
return (actions + ((b, None, tmp_reg, ref.offset),), tmp_reg, o)
890930
assert False, ref
891931

892932
def as_str(base, offset):
893-
return base if offset is None else '[%s%s]' % (base, '+0x%x'%offset if offset else '')
933+
off = ('+0x%x' % offset if offset >= 0 else '-0x%x' % abs(offset)) \
934+
if offset is not None else ''
935+
return base if offset is None else '[%s%s]' % (base, off)
894936

895937
def move(src, src_off, dest, dest_off):
896938
comment = '\tMOV %s, %s' % (as_str(src, src_off), as_str(dest, dest_off))
@@ -912,7 +954,12 @@ def act_out(steps):
912954
actions, base, offset = steps
913955
base = str(base) # Convert any memory reference to string
914956
for action in actions:
915-
a_base, a_off, a_dest = action
957+
a_base, a_off, a_dest, *extra = action
958+
if extra: # IndirectOffset
959+
assert type(a_base) != int, 'unsupported array reference'
960+
move(a_base, None, a_dest, None)
961+
self.write('ADD', extra[0], a_dest)
962+
continue
916963
move(a_base, a_off, a_dest, None)
917964
return (base, offset)
918965

@@ -935,6 +982,9 @@ def move(self, src, dest):
935982
size = 1
936983
elif isinstance(src, Dereference):
937984
size = 1
985+
elif isinstance(src.type, ArrayType):
986+
# Just reference first offset in array
987+
size = src.type.type.size
938988
else:
939989
size = src.type.size
940990
src_addr = self.load_address(src)
@@ -973,9 +1023,12 @@ def load_address(self, ref):
9731023
if isinstance(ref, Global):
9741024
return Direct(ref.loc)
9751025
if isinstance(ref, ArrayElement):
976-
return Offset(ref=self.load_address(ref.array),
977-
offset=ref.index*ref.type.type.size)
978-
if isinstance(ref, (Relative, Direct, Offset, Indirect)):
1026+
a_ref = self.load_address(ref.array)
1027+
if type(ref.index) == int:
1028+
return Offset(ref=a_ref, offset=ref.index*ref.type.size)
1029+
else:
1030+
return IndirectOffset(ref=a_ref, offset=ref.index)
1031+
if isinstance(ref, (Relative, Direct, Offset, Indirect, IndirectOffset)):
9791032
return ref
9801033
assert False, type(ref)
9811034

@@ -1334,7 +1387,12 @@ def visit_arr_subscript_expr(self, expr):
13341387
array = self.visit_expression(expr.expr)
13351388
index = self.visit_expression(expr.sub)
13361389
assert isinstance(array.type, ArrayType), type(array.type)
1337-
assert type(index) == int, type(index) # TODO non-constant index
1390+
if type(index) != int:
1391+
tmp = self.new_temporary_var()
1392+
with self.mutate(tmp, write_only=True) as ref:
1393+
self.write('MOV', index, ref)
1394+
self.write('MUL', array.type.type.size, ref)
1395+
index = ref # unsafe expose of ref
13381396
return ArrayElement(array=array, index=index, type=array.type.type)
13391397

13401398
def visit_unary_expr(self, expr):
@@ -1400,7 +1458,7 @@ def visit_func_call_expr(self, expr):
14001458
name = expr.ref.val
14011459
if name in self.optimized_functions:
14021460
return self.optimized_functions[name](expr)
1403-
assert name in self.functions
1461+
assert name in self.functions, "Unknown function %s" % name
14041462
func = self.functions[name]
14051463
assert len(expr.args) == len(func.param_types)
14061464
large_ret = func.ret_type.size > 1 # Can't fit in single register
@@ -1415,7 +1473,7 @@ def visit_func_call_expr(self, expr):
14151473
for arg in expr.args:
14161474
var = self.visit_expression(arg)
14171475
self.write('MOV', var, Relative(rel_to='cbp', offset=i))
1418-
if type(var) == int:
1476+
if type(var) in [int, str] or isinstance(var, Register):
14191477
i += 1
14201478
else:
14211479
i += var.type.size

examples/hdd_driver.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* hdd_driver.c - Functionally equivalent to hdd_driver.asm
3+
*/
4+
#include "mclib.h"
5+
6+
#define MEM_SIZE_X 8
7+
#define MEM_SIZE_Z 8
8+
9+
#define WORD_SIZE 8
10+
11+
int mar;
12+
int mbr;
13+
14+
int __hdd_addr;
15+
int __hdd_mul;
16+
17+
void memory_seek();
18+
19+
void read_mem() {
20+
CMD(summon armor_stand $arg:mem_loc$ {Tags:["$tag:_mem_ptr"], NoGravity:1b, Marker: 1b});
21+
__hdd_addr = mar;
22+
mbr = 0;
23+
__hdd_addr /= MEM_SIZE_X;
24+
memory_seek();
25+
__hdd_addr = WORD_SIZE;
26+
__hdd_mul = 1;
27+
28+
while (__hdd_addr) {
29+
if(TEST_CMD(execute @e[tag=$tag:_mem_ptr$] ~ ~ ~ testforblock ~ ~ ~ stone)) {
30+
mbr += __hdd_mul;
31+
}
32+
__hdd_addr -= 1;
33+
__hdd_mul *= 2;
34+
CMD(tp @e[tag=$tag:_mem_ptr$] ~ ~1 ~);
35+
}
36+
CMD(kill @e[tag=$tag:_mem_ptr$]);
37+
}
38+
39+
int __mem_temp;
40+
41+
void write_mem() {
42+
CMD(summon armor_stand $arg:mem_loc$ {Tags:["$tag:_mem_ptr"], NoGravity:1b, Marker: 1b});
43+
__hdd_addr = mar;
44+
__hdd_addr /= MEM_SIZE_X;
45+
memory_seek();
46+
__hdd_addr = WORD_SIZE;
47+
__hdd_mul = mbr;
48+
49+
while (__hdd_addr) {
50+
__mem_temp = __hdd_mul;
51+
__mem_temp %= 2;
52+
__hdd_addr -= 1;
53+
__hdd_mul /= 2;
54+
if (__mem_temp == 0) {
55+
CMD(execute @e[tag=$tag:_mem_ptr$] ~ ~ ~ setblock ~ ~ ~ air);
56+
} else {
57+
CMD(execute @e[tag=$tag:_mem_ptr$] ~ ~ ~ setblock ~ ~ ~ stone);
58+
}
59+
CMD(tp @e[tag=$tag:_mem_ptr$] ~ ~1 ~);
60+
}
61+
CMD(kill @e[tag=$tag:_mem_ptr$]);
62+
}
63+
64+
void memory_seek() {
65+
while (__hdd_addr) {
66+
CMD(tp @e[tag=$tag:_mem_ptr$] ~1 ~ ~);
67+
__hdd_addr -= 1;
68+
}
69+
70+
__hdd_addr = mar;
71+
__hdd_addr %= MEM_SIZE_Z;
72+
73+
while (__hdd_addr) {
74+
__hdd_addr -= 1;
75+
CMD(tp @e[tag=$tag:_mem_ptr$] ~ ~ ~1);
76+
}
77+
}
78+

examples/mclib.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* mclib.h
3+
*
4+
* Common library utilities.
5+
*/
6+
7+
8+
#ifndef __MCLIB_H
9+
#define __MCLIB_H 1
10+
11+
/**
12+
* Stringify (add quotes to) the bare word argument
13+
*/
14+
#define __str_quote(...) #__VA_ARGS__
15+
16+
/**
17+
* Wrapper for __test_command allowing bare words
18+
*/
19+
#define TEST_CMD(...) __test_command(__str_quote(__VA_ARGS__))
20+
21+
/**
22+
* Inject the command (bare words) into the resulting ASM
23+
*/
24+
#define CMD(...) __asm__(__str_quote(CMD __VA_ARGS__))
25+
26+
/**
27+
* Swaps the value of the two arguments without the use of a temporary variable
28+
*/
29+
#define ASM_swap(a, b) __asm__("XCHG >?, >?", a, b)
30+
31+
#endif /* __MCLIB_H */

0 commit comments

Comments
 (0)