|
// cc -std=c11 miniast.c -o miniast && ./miniast |
|
#include "xcommon.h" |
|
ASSUME_NONNULL_BEGIN |
|
|
|
typedef enum AKind { |
|
AKindInt, // 123 |
|
AKindAdd, // + |
|
AKindSub, // - |
|
AKindMul, // * |
|
} AKind; |
|
|
|
typedef u16 AOffs; |
|
|
|
#define AALIGN alignof(AOffs) |
|
|
|
typedef struct AInt { |
|
u8 kind; |
|
u8 valClass; |
|
u16 val; // literal value when valClass == AIntValClass_i16 |
|
} AInt; |
|
|
|
enum AIntValClass { |
|
AIntValClass_i16 = 0, |
|
AIntValClass_i32 = 1, |
|
AIntValClass_i64 = 2, |
|
AIntValClass_i128 = 3, |
|
}; |
|
|
|
typedef struct ABinOp { |
|
u8 kind; |
|
u8 _unused; |
|
AOffs op1; |
|
// op2 is always offset 0 |
|
} ABinOp; |
|
|
|
|
|
u8 __attribute__((aligned(AALIGN))) astack_base[1024*1024]; |
|
void* astack_top = (void*)astack_base + sizeof(astack_base); |
|
|
|
|
|
void* AStackAllocBytes(usize nbyte) { |
|
assert((uintptr)astack_top - nbyte >= (uintptr)astack_base); // else out of memory |
|
astack_top -= ALIGN2(nbyte, AALIGN); |
|
return astack_top; |
|
} |
|
|
|
#define AStackAlloc(STRUCT_TYPE, KIND) ({ \ |
|
STRUCT_TYPE* n__##__LINE__ = AStackAllocBytes(sizeof(STRUCT_TYPE)); \ |
|
n__##__LINE__->kind = KIND; \ |
|
n__##__LINE__; \ |
|
}) |
|
|
|
usize AStackUsage() { |
|
return (usize)(((uintptr)astack_base + sizeof(astack_base)) - (uintptr)astack_top); |
|
} |
|
|
|
const char* StrOfAKind(AKind kind) { |
|
switch (kind) { |
|
case AKindInt: return "int"; |
|
case AKindAdd: return "add"; |
|
case AKindSub: return "sub"; |
|
case AKindMul: return "mul"; |
|
} |
|
static char tmpbuf[16]; |
|
sprintf(tmpbuf, "?(%u)", kind); |
|
return tmpbuf; |
|
} |
|
|
|
AKind AKindOf(const void* node) { return *(u8*)node; } |
|
|
|
// AOffsOfChild returns the offset to child from parent |
|
AOffs AOffsOfChild(const void* parent, usize parent_nbyte, const void* child) { |
|
assert((uintptr)parent % AALIGN == 0); |
|
assert(parent_nbyte % AALIGN == 0); |
|
assert((uintptr)child % AALIGN == 0); |
|
// astack_base[ ... {parent} {child2} {child1} ]astack_top |
|
// |~~~~~~~~~~| |
|
// result is the distance in multiples of AALIGN from child1 to end of parent |
|
uintptr parent_end = (uintptr)parent + parent_nbyte; |
|
assert((uintptr)child - parent_end <= U16_MAX); // check for overflow & order |
|
return ((uintptr)child - parent_end) / AALIGN; |
|
} |
|
|
|
const void* ChildOfAOffs(const void* parent, usize parent_nbyte, AOffs child_offs) { |
|
assert((uintptr)parent % AALIGN == 0); |
|
assert(parent_nbyte % AALIGN == 0); |
|
return (parent + parent_nbyte) + ((uintptr)child_offs * AALIGN); |
|
} |
|
|
|
#define CHILD(parentp, child_offs) \ |
|
ChildOfAOffs((parentp), sizeof(*(parentp)), child_offs) |
|
|
|
AInt* PushAInt16(u16 val) { |
|
AInt* n = AStackAlloc(AInt, AKindInt); |
|
n->valClass = AIntValClass_i16; |
|
n->val = val; assert(val <= U16_MAX); |
|
return n; |
|
} |
|
|
|
ABinOp* PushABinOp(u8 kind, const void* op1, const void* op2) { |
|
ABinOp* n = AStackAlloc(ABinOp, kind); |
|
n->op1 = AOffsOfChild(n, sizeof(ABinOp), op1); |
|
assert(AOffsOfChild(n, sizeof(ABinOp), op2) == 0); // op2 is implicit (adjacent to n) |
|
return n; |
|
} |
|
|
|
void printstr(const char* cstr) { fwrite(cstr, strlen(cstr), 1, stdout); } |
|
|
|
void _APrint(const void* n, int maxdepth, int depth) { |
|
if (depth) printf("\n%*.s", depth*2, ""); |
|
printf("(%s ", StrOfAKind(AKindOf(n))); |
|
switch (AKindOf(n)) { |
|
case AKindInt: printf("%u", ((AInt*)n)->val); goto end; |
|
case AKindAdd: |
|
case AKindSub: |
|
case AKindMul: { |
|
if (depth >= maxdepth) break; |
|
const ABinOp* binop = n; |
|
_APrint(CHILD(binop, binop->op1), maxdepth, depth+1); |
|
putc(' ', stdout); |
|
_APrint(CHILD(binop, 0), maxdepth, depth+1); |
|
goto end; |
|
} |
|
} |
|
printstr("…"); |
|
end: |
|
fwrite(")\n", 2 - (int)(depth > 0), 1, stdout); |
|
} |
|
|
|
void APrint(const void* n, int maxdepth) { |
|
return _APrint(n, maxdepth < 0 ? I32_MAX : maxdepth, 0); |
|
} |
|
|
|
void* nullable AEval(void* SB, void* SP, const void* n); |
|
|
|
u64 AEvalToI64(void* SB, void* SP, const void* n) { |
|
if (AKindOf(n) == AKindInt) return (u64)((AInt*)n)->val; |
|
AInt* res = AEval(SB, SP, n); |
|
assert(AKindOf(res) == AKindInt); |
|
return (u64)res->val; |
|
} |
|
|
|
void* nullable AEval(void* SB, void* SP, const void* n) { |
|
assert((uintptr)SP % AALIGN == 0); |
|
//printf("eval [SP %p] ", SP); APrint(n, 1); |
|
switch (AKindOf(n)) { |
|
case AKindInt: { |
|
AInt* res = SP - sizeof(AInt); assert((uintptr)res >= (uintptr)SB); |
|
memcpy(res, n, sizeof(AInt)); |
|
return res; |
|
} |
|
case AKindAdd: |
|
case AKindSub: |
|
case AKindMul: { |
|
const ABinOp* binop = n; |
|
u64 x, y; |
|
x = AEvalToI64(SB, SP - sizeof(AInt), CHILD(binop, binop->op1)); |
|
y = AEvalToI64(SB, SP, CHILD(binop, 0)); |
|
if (AKindOf(n) == AKindAdd) x = x + y; |
|
else if (AKindOf(n) == AKindSub) x = x - y; |
|
else if (AKindOf(n) == AKindMul) x = x * y; |
|
assert(x <= (1<<14)-1); // TODO: valClass |
|
AInt* res = SP - sizeof(AInt); assert((uintptr)res >= (uintptr)SB); |
|
res->kind = AKindInt; |
|
res->val = (u16)x; |
|
//printf(" res "); APrint(res, -1); |
|
return res; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
int main(int argc, char* argv[]) { |
|
/* |
|
┌───────────────┐ mul |
|
│ 1 + 2 * 6 - 3 │ ⟶ ╱ ╲ |
|
└───────────────┘ add sub |
|
int int int int |
|
1 2 6 3 |
|
|
|
memory representation: |
|
┌──────┬───────────────────────┐ |
|
│ u32: │ 0 │ |
|
│ u16: │ 0 ╷ 1 │ |
|
│ u8: │ 0 ╷ 1 │ 2 ╷ 3 │ |
|
├──────┼─────┼─────┼─────┴─────┤ |
|
│ ~ ~ ~ ~ |
|
│ FFE7 │ mul │ │ 12 │ 12 = operand 1 offset (FFF7) |
|
├──────┼─────┼─────┼───────────┤ |
|
│ FFEB │ sub │ │ 4 │ 4 = operand 1 offset (FFF3) |
|
│ FFEF │ int │ 0 │ 3 │ 0 = small, 3 = value |
|
│ FFF3 │ int │ 0 │ 6 │ 0 = small, 6 = value |
|
├──────┼─────┼─────┼───────────┤ |
|
│ FFF7 │ add │ │ 4 │ 4 = operand 1 offset (FFFF) |
|
│ FFFB │ int │ 0 │ 2 │ 0 = small, 2 = value |
|
│ FFFF │ int │ 0 │ 1 │ 0 = small, 1 = value |
|
└──────┴─────┴─────┴───────────┘ |
|
|
|
*/ |
|
|
|
AInt* i_1 = PushAInt16(1); |
|
AInt* i_2 = PushAInt16(2); |
|
ABinOp* add = PushABinOp(AKindAdd, i_1, i_2); |
|
|
|
AInt* i_8 = PushAInt16(8); |
|
AInt* i_4 = PushAInt16(4); |
|
ABinOp* sub = PushABinOp(AKindSub, i_8, i_4); |
|
|
|
ABinOp* mul = PushABinOp(AKindMul, add, sub); |
|
|
|
printf("AST: (%zu B of memory)\n", AStackUsage()); |
|
APrint(mul, -1); |
|
|
|
u8 __attribute__((aligned(AALIGN))) stack[1024]; |
|
void* res = AEval(stack, stack+sizeof(stack), mul); |
|
printf("result: "); APrint(res, -1); |
|
|
|
return 0; |
|
} |
|
|
|
ASSUME_NONNULL_END |