replace quad by ssa ir
Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
parent
003a5215a8
commit
376c7c3eeb
4
.gitignore
vendored
4
.gitignore
vendored
@ -47,4 +47,8 @@ out.txt
|
|||||||
|
|
||||||
# builder
|
# builder
|
||||||
.xmake/
|
.xmake/
|
||||||
|
.cache/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
# Language Server
|
||||||
|
compile_commands.json
|
||||||
|
116
include/acir.h
116
include/acir.h
@ -1,26 +1,118 @@
|
|||||||
#ifndef ACC_ACIR_H
|
#ifndef ACC_ACIR_H
|
||||||
#define ACC_ACIR_H
|
#define ACC_ACIR_H
|
||||||
|
|
||||||
// operations in the ACC IR(ACIR)
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ast.h"
|
||||||
|
#include "util/linklist.h"
|
||||||
|
|
||||||
|
// Operation code definations in the ACC IR(ACIR).
|
||||||
enum {
|
enum {
|
||||||
IR_ADD, IR_SUB, IR_MUL, IR_DIV,
|
// Loads
|
||||||
IR_EQ, IR_NE, IR_LT, IR_GT, IR_LE, IR_GE,
|
IR_IMM_I32, // immediate integer (32bits)
|
||||||
IR_NEG,
|
|
||||||
IR_LIT32, IR_LIT64,
|
// SSA
|
||||||
|
IR_PHI, // phi node in SSA
|
||||||
|
|
||||||
|
// Arithmetic operations
|
||||||
|
IR_NEG, // negation
|
||||||
|
IR_NOT, // bitwise not
|
||||||
|
IR_CMP_EQ, // compare whether equal
|
||||||
|
|
||||||
|
// Terminates
|
||||||
|
IR_RET, // return
|
||||||
|
IR_JMP, // jump: always goto true branch
|
||||||
|
IR_BR, // conditional jump
|
||||||
|
|
||||||
|
// Guard
|
||||||
|
IR_NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IRblock {
|
// Argument of phi function.
|
||||||
|
struct IRphi_arg {
|
||||||
}
|
struct llist_node n; // linklist header
|
||||||
|
struct IRblock *source; // predecessor basic block
|
||||||
|
struct IRinstruction *value; // corresponding value when comming from the block
|
||||||
|
};
|
||||||
|
|
||||||
|
// IR instruction.
|
||||||
|
// The result is represented as itself.
|
||||||
struct IRinstruction {
|
struct IRinstruction {
|
||||||
int op;
|
struct llist_node n; // linklist header
|
||||||
|
int op; // operation code
|
||||||
|
int id; // value identifier
|
||||||
|
struct IRblock *owner; // the basic block containing this instruction
|
||||||
union {
|
union {
|
||||||
struct { int left, right; };
|
struct { struct IRinstruction *left, *right; }; // left/right operands for calculations
|
||||||
int val_i32;
|
struct { struct IRinstruction *cond; // jump condition
|
||||||
int val_i64;
|
struct IRblock *bt, *bf; }; // true branch & false branch for conditional jump
|
||||||
|
struct linklist phi; // Phi instruction argument list
|
||||||
|
int32_t val_i32; // immediate: integer (32bits)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// IR basic block
|
||||||
|
struct IRblock {
|
||||||
|
struct llist_node n; // linklist header
|
||||||
|
int id; // block identifier, the function entry block will have value 0
|
||||||
|
struct linklist ins; // contained instructions
|
||||||
|
bool is_complete; // whether the block is properly ended with a terminate
|
||||||
|
struct linklist pre; // predecessor list of this basic block
|
||||||
|
struct IRfunction *owner; // the function containing this function
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function containing IR instructions.
|
||||||
|
// TODO: paramaters
|
||||||
|
struct IRfunction {
|
||||||
|
struct llist_node n; // linklist header
|
||||||
|
char *name; // function name
|
||||||
|
struct linklist bs; // basic blocks
|
||||||
|
int ins_count; // number of instructions, used for allocating instruction identifier.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs an IRinstruction with an operator, and two operands.
|
||||||
|
struct IRinstruction* IRinstruction_new(struct IRblock *owner, int op,
|
||||||
|
struct IRinstruction *left, struct IRinstruction *right);
|
||||||
|
|
||||||
|
// Constructs a IRinstruction with an integer immediate (32bits).
|
||||||
|
struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v);
|
||||||
|
|
||||||
|
// Constructs a IRinstuction with instruction Q_JMP or Q_BR (which is conditional jump).
|
||||||
|
struct IRinstruction* IRinstruction_new_jmp(struct IRblock *owner, int op, struct IRinstruction *cond,
|
||||||
|
struct IRblock *bt, struct IRblock *bf);
|
||||||
|
|
||||||
|
// Returns whether an IR opcode is a terminate.
|
||||||
|
// Terminate must and may only appear exactly once at ther end of each basic block.
|
||||||
|
bool IRis_terminate(int op);
|
||||||
|
|
||||||
|
// Returns whether an IR opcode is a jump opcode.
|
||||||
|
bool IRis_jmp(int op);
|
||||||
|
|
||||||
|
// Constructs a IRblock.
|
||||||
|
struct IRblock* IRblock_new(struct IRfunction *owner);
|
||||||
|
|
||||||
|
// Allocating instruction identifiers in IRfunction
|
||||||
|
int IRfunction_alloc_ins(struct IRfunction *self);
|
||||||
|
|
||||||
|
// Generates Quad Repersentation from an AST
|
||||||
|
struct IRfunction* IRfunction_from_ast(struct Afunction *afunc);
|
||||||
|
|
||||||
|
// Frees a IRinstruction and all its components.
|
||||||
|
void IRinstruction_free(struct IRinstruction *self);
|
||||||
|
|
||||||
|
// Frees a IRblock and all its components.
|
||||||
|
void IRblock_free(struct IRblock *self);
|
||||||
|
|
||||||
|
// Frees a IRfunction and all its components.
|
||||||
|
void IRfunction_free(struct IRfunction *self);
|
||||||
|
|
||||||
|
// Outputs the instruction.
|
||||||
|
void IRinstruction_print(struct IRinstruction *self, FILE *Outfile);
|
||||||
|
|
||||||
|
// Outputs the containing instructions of the IRblock.
|
||||||
|
void IRblock_print(struct IRblock *self, FILE *Outfile);
|
||||||
|
|
||||||
|
// Outputs the containing instructions of the IRfunction.
|
||||||
|
void IRfunction_print(struct IRfunction *self, FILE *Outfile);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef ACC_AST_H
|
#ifndef ACC_AST_H
|
||||||
#define ACC_AST_H
|
#define ACC_AST_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "util/linklist.h"
|
#include "util/linklist.h"
|
||||||
|
|
||||||
@ -114,5 +115,8 @@ void afunc_debug_print(FILE *Outfile, struct Afunction *f);
|
|||||||
void afunc_free(struct Afunction *f);
|
void afunc_free(struct Afunction *f);
|
||||||
void ast_free(struct ASTnode *x);
|
void ast_free(struct ASTnode *x);
|
||||||
|
|
||||||
|
// Parse source into AST.
|
||||||
|
struct Afunction* Afunction_from_source(const char *filename);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdnoreturn.h>
|
#include <stdnoreturn.h>
|
||||||
|
|
||||||
void* malloc_or_fail(size_t s, const char *func_name);
|
|
||||||
noreturn void fail_target(const char *target_name);
|
noreturn void fail_target(const char *target_name);
|
||||||
noreturn void fail_malloc(const char *func_name);
|
noreturn void fail_malloc(const char *func_name);
|
||||||
noreturn void fail_quad_op(int op, const char *func_name);
|
noreturn void fail_ir_op(int op, const char *func_name);
|
||||||
noreturn void fail_ast_op(int op, const char *func_name);
|
noreturn void fail_ast_op(int op, const char *func_name);
|
||||||
noreturn void fail_ce_expect(int line, const char *expected, const char *got);
|
noreturn void fail_ce_expect(int line, const char *expected, const char *got);
|
||||||
noreturn void fail_ce(int line, const char *reason);
|
noreturn void fail_ce(int line, const char *reason);
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#ifndef ACC_PARSE_H
|
|
||||||
#define ACC_PARSE_H
|
|
||||||
|
|
||||||
struct Afunction* parse_source(const char *filename);
|
|
||||||
|
|
||||||
#endif
|
|
101
include/quad.h
101
include/quad.h
@ -1,101 +0,0 @@
|
|||||||
#ifndef ACC_QUAD_H
|
|
||||||
#define ACC_QUAD_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "ast.h"
|
|
||||||
#include "util/linklist.h"
|
|
||||||
|
|
||||||
// Operation code definations
|
|
||||||
enum {
|
|
||||||
// Loading immediates
|
|
||||||
Q_IMM_I32, // integer (32bits)
|
|
||||||
|
|
||||||
// Arithmetic operations
|
|
||||||
Q_NEG, // negation
|
|
||||||
Q_NOT, // bitwise not
|
|
||||||
Q_CMP_EQ, // compare whether equal
|
|
||||||
|
|
||||||
// Terminates
|
|
||||||
Q_RET, // return
|
|
||||||
Q_BR_ALWAYS, // conditional goto: always goto true branch.
|
|
||||||
|
|
||||||
// Guard
|
|
||||||
Q_NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Local(in function) variable.
|
|
||||||
struct Qvar {
|
|
||||||
struct llist_node n; // linklist header
|
|
||||||
int id; // variable id
|
|
||||||
};
|
|
||||||
|
|
||||||
// Quad instruction.
|
|
||||||
struct Quad {
|
|
||||||
struct llist_node n; // linklist header
|
|
||||||
int op; // operation code
|
|
||||||
struct Qvar *dest; // operation destination
|
|
||||||
union {
|
|
||||||
struct { struct Qvar *left, *right; }; // left/right operands for calculations
|
|
||||||
struct { struct Qblock *bt, *bf; }; // true branch & false branch for conditional goto
|
|
||||||
int32_t val_i32; // immediate: integer (32bits)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Basic block consist of Quad instructions.
|
|
||||||
struct Qblock {
|
|
||||||
struct llist_node n; // linklist header
|
|
||||||
int id; // block id
|
|
||||||
struct linklist ins; // instruction Quads
|
|
||||||
bool is_complete; // whether the block is properly ended with a terminate
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function containing Quad instructions.
|
|
||||||
// TODO: paramaters
|
|
||||||
struct Qfunction {
|
|
||||||
struct llist_node n; // linklist header
|
|
||||||
char *name; // function name
|
|
||||||
struct linklist bs; // basic blocks
|
|
||||||
struct linklist vars; // local variables
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructs a Quad with an operator, a destination and two operands.
|
|
||||||
struct Quad* quad_make(int op, struct Qvar *dest, struct Qvar *left, struct Qvar *right);
|
|
||||||
|
|
||||||
// Constructs a Quad with a loads an integer immediate (32bits).
|
|
||||||
struct Quad* quad_make_i32(struct Qvar *dest, int32_t v);
|
|
||||||
|
|
||||||
// Constructs a Quad with instruction Q_BR_XXX (conditional goto).
|
|
||||||
struct Quad* quad_make_br(int op, struct Qblock *bt, struct Qblock *bf);
|
|
||||||
|
|
||||||
// Returns whether the given opcode is a opcode from a terminate.
|
|
||||||
bool quad_is_terminate(int op);
|
|
||||||
|
|
||||||
// Constructs a Qblock.
|
|
||||||
struct Qblock* qblock_make();
|
|
||||||
|
|
||||||
// Allocates a new basic block in the give function.
|
|
||||||
struct Qblock* qfunc_new_block(struct Qfunction *f);
|
|
||||||
|
|
||||||
// Appends an instruction to a block.
|
|
||||||
void qblock_add_ins(struct Qblock *b, struct Quad *x);
|
|
||||||
|
|
||||||
// Constructs a new Qvar.
|
|
||||||
struct Qvar* qvar_make(int id);
|
|
||||||
|
|
||||||
// Allocates a new variable in the given function.
|
|
||||||
struct Qvar* qfunc_new_var(struct Qfunction *f);
|
|
||||||
|
|
||||||
// Generates Quad Repersentation from an AST
|
|
||||||
struct Qfunction* qfunc_cgenerate(struct Afunction *afunc);
|
|
||||||
|
|
||||||
// Frees a Qblock and all its components.
|
|
||||||
void qblock_free(struct Qblock *b);
|
|
||||||
|
|
||||||
// Frees a Qfunction and all its components.
|
|
||||||
void qfunc_free(struct Qfunction *f);
|
|
||||||
|
|
||||||
// Prints the contents of a Qfunction for debugging.
|
|
||||||
void qfunc_debug_print(struct Qfunction *self, FILE *Outfile);
|
|
||||||
|
|
||||||
#endif
|
|
@ -4,7 +4,7 @@
|
|||||||
// Target types
|
// Target types
|
||||||
enum {
|
enum {
|
||||||
TARGET_AST,
|
TARGET_AST,
|
||||||
TARGET_QUAD,
|
TARGET_ACIR,
|
||||||
TARGET_NULL,
|
TARGET_NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#define ACC_ARRAY_LENGTH(a) (sizeof((a))/sizeof(*(a)))
|
#define ACC_ARRAY_LENGTH(a) (sizeof((a))/sizeof(*(a)))
|
||||||
|
|
||||||
|
void* malloc_or_fail(size_t s, const char *func_name);
|
||||||
bool strequal(const char *s1, const char *s2);
|
bool strequal(const char *s1, const char *s2);
|
||||||
char* strclone(const char *s);
|
char* strclone(const char *s);
|
||||||
|
|
||||||
|
13
main.c
13
main.c
@ -2,10 +2,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "scan.h"
|
#include "scan.h"
|
||||||
#include "parse.h"
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
#include "quad.h"
|
#include "acir.h"
|
||||||
|
|
||||||
// Print out a usage if started incorrectly
|
// Print out a usage if started incorrectly
|
||||||
static void usage(char *prog) {
|
static void usage(char *prog) {
|
||||||
@ -36,13 +35,13 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int target = target_parse(argv[1]);
|
int target = target_parse(argv[1]);
|
||||||
struct Afunction *afunc = parse_source(argv[2]);
|
struct Afunction *afunc = Afunction_from_source(argv[2]);
|
||||||
if (target == TARGET_AST) {
|
if (target == TARGET_AST) {
|
||||||
afunc_debug_print(Outfile, afunc);
|
afunc_debug_print(Outfile, afunc);
|
||||||
} else if (target == TARGET_QUAD) {
|
} else if (target == TARGET_ACIR) {
|
||||||
struct Qfunction *qfunc = qfunc_cgenerate(afunc);
|
struct IRfunction *ir = IRfunction_from_ast(afunc);
|
||||||
qfunc_debug_print(qfunc, Outfile);
|
IRfunction_print(ir, Outfile);
|
||||||
qfunc_free(qfunc);
|
IRfunction_free(ir);
|
||||||
}
|
}
|
||||||
afunc_free(afunc);
|
afunc_free(afunc);
|
||||||
return (0);
|
return (0);
|
||||||
|
264
src/acir.c
Normal file
264
src/acir.c
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include "util/misc.h"
|
||||||
|
#include "fatals.h"
|
||||||
|
#include "acir.h"
|
||||||
|
|
||||||
|
#define IRinstruction_constructor_shared_code \
|
||||||
|
struct IRinstruction *self = malloc_or_fail(sizeof(struct IRinstruction), __FUNCTION__); \
|
||||||
|
self->id = IRfunction_alloc_ins(owner->owner); \
|
||||||
|
self->owner = owner; \
|
||||||
|
IRblock_add_ins(owner, self); \
|
||||||
|
|
||||||
|
// Adds one instruction to list.
|
||||||
|
// Internal function only: IRinstruction_new_xxx() automaticly calls this function.
|
||||||
|
static void IRblock_add_ins(struct IRblock *self, struct IRinstruction *x) {
|
||||||
|
if (self->is_complete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
llist_pushback(&self->ins, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs an IRinstruction with an operator, and two operands.
|
||||||
|
struct IRinstruction* IRinstruction_new(struct IRblock *owner, int op,
|
||||||
|
struct IRinstruction *left, struct IRinstruction *right) {
|
||||||
|
IRinstruction_constructor_shared_code
|
||||||
|
|
||||||
|
self->op = op;
|
||||||
|
self->left = left;
|
||||||
|
self->right = right;
|
||||||
|
|
||||||
|
if (IRis_terminate(self->op)) {
|
||||||
|
owner->is_complete = true;
|
||||||
|
}
|
||||||
|
return (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a IRinstruction with an integer immediate (32bits).
|
||||||
|
struct IRinstruction* IRinstruction_new_i32(struct IRblock *owner, int32_t v) {
|
||||||
|
IRinstruction_constructor_shared_code
|
||||||
|
|
||||||
|
self->op = IR_IMM_I32;
|
||||||
|
self->val_i32 = v;
|
||||||
|
return (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a IRinstuction with instruction Q_JMP or Q_BR (which is conditional jump).
|
||||||
|
struct IRinstruction* IRinstruction_new_jmp(struct IRblock *owner, int op, struct IRinstruction *cond,
|
||||||
|
struct IRblock *bt, struct IRblock *bf) {
|
||||||
|
IRinstruction_constructor_shared_code
|
||||||
|
|
||||||
|
self->op = op;
|
||||||
|
self->cond = cond;
|
||||||
|
self->bt = bt;
|
||||||
|
self->bf = bf;
|
||||||
|
owner->is_complete = true;
|
||||||
|
|
||||||
|
if (bt) {
|
||||||
|
llist_pushback(&bt->pre, owner);
|
||||||
|
}
|
||||||
|
if (bf) {
|
||||||
|
llist_pushback(&bf->pre, owner);
|
||||||
|
}
|
||||||
|
return (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether an IR opcode is a terminate.
|
||||||
|
// Terminate must and may only appear exactly once at ther end of each basic block.
|
||||||
|
bool IRis_terminate(int op) {
|
||||||
|
switch (op) {
|
||||||
|
case IR_RET: case IR_BR: case IR_JMP:
|
||||||
|
return (true);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether an IR opcode is a jump opcode.
|
||||||
|
bool IRis_jmp(int op) {
|
||||||
|
switch (op) {
|
||||||
|
case IR_JMP: case IR_BR:
|
||||||
|
return (true);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a IRblock.
|
||||||
|
struct IRblock* IRblock_new(struct IRfunction *owner) {
|
||||||
|
struct IRblock *self = malloc_or_fail(sizeof(struct IRblock), __FUNCTION__);
|
||||||
|
|
||||||
|
self->id = owner->bs.length;
|
||||||
|
self->owner = owner;
|
||||||
|
self->is_complete = false;
|
||||||
|
llist_init(&self->pre);
|
||||||
|
llist_init(&self->ins);
|
||||||
|
llist_pushback(&owner->bs, self);
|
||||||
|
return (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocating instruction identifiers in IRfunction
|
||||||
|
int IRfunction_alloc_ins(struct IRfunction *self) {
|
||||||
|
return self->ins_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate an AST unary arithmetic opcode to a IR opcode.
|
||||||
|
static int translate_ast_unary_op(int op) {
|
||||||
|
switch (op) {
|
||||||
|
case A_NEG: return (IR_NEG);
|
||||||
|
case A_BNOT: return (IR_NOT);
|
||||||
|
default: fail_ir_op(op, __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct IRinstruction* IRcg_dfs(struct ASTnode *x, struct IRfunction *f, struct IRblock *b) {
|
||||||
|
if (x == NULL) {
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (x->op) {
|
||||||
|
case A_RETURN: {
|
||||||
|
struct ASTunnode *t = (void*)x;
|
||||||
|
struct IRinstruction *value = IRcg_dfs(t->left, f, b);
|
||||||
|
IRinstruction_new(b, IR_RET, value, NULL);
|
||||||
|
b->is_complete = true;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
case A_BLOCK: {
|
||||||
|
struct ASTblocknode *t = (void*)x;
|
||||||
|
struct llist_node *p = t->st.head;
|
||||||
|
while (p) {
|
||||||
|
IRcg_dfs((struct ASTnode*)p, f, b);
|
||||||
|
p = p->nxt;
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
case A_LIT_I32: {
|
||||||
|
struct ASTi32node *t = (void*)x;
|
||||||
|
return (IRinstruction_new_i32(b, t->val));
|
||||||
|
}
|
||||||
|
|
||||||
|
case A_NEG: case A_BNOT: {
|
||||||
|
struct ASTunnode *t = (void*)x;
|
||||||
|
struct IRinstruction *value = IRcg_dfs(t->left, f, b);
|
||||||
|
return (IRinstruction_new(b, translate_ast_unary_op(x->op), value, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
case A_LNOT: {
|
||||||
|
struct ASTunnode *t = (void*)x;
|
||||||
|
struct IRinstruction *value = IRcg_dfs(t->left, f, b),
|
||||||
|
*zero = IRinstruction_new_i32(b, 0);
|
||||||
|
return (IRinstruction_new(b, IR_CMP_EQ, value, zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
fail_ast_op(x->op, __FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates Quad Repersentation from an AST
|
||||||
|
struct IRfunction* IRfunction_from_ast(struct Afunction *afunc) {
|
||||||
|
struct IRfunction *self = malloc_or_fail(sizeof(struct IRfunction), __FUNCTION__);
|
||||||
|
|
||||||
|
self->name = afunc->name; // transfer ownership of function name string
|
||||||
|
afunc->name = NULL; // prevents the pointer being freed when freeing the Afunction
|
||||||
|
|
||||||
|
self->ins_count = 0;
|
||||||
|
llist_init(&self->bs);
|
||||||
|
|
||||||
|
struct IRblock *entry = IRblock_new(self);
|
||||||
|
IRcg_dfs(afunc->rt, self, entry);
|
||||||
|
return (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees a IRinstruction and all its components.
|
||||||
|
void IRinstruction_free(struct IRinstruction *self) {
|
||||||
|
if (self->op == IR_PHI) {
|
||||||
|
llist_free(&self->phi);
|
||||||
|
}
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees a IRblock and all its components.
|
||||||
|
void IRblock_free(struct IRblock *self) {
|
||||||
|
struct llist_node *p = self->ins.head, *nxt;
|
||||||
|
while (p) {
|
||||||
|
nxt = p->nxt;
|
||||||
|
IRinstruction_free((void*)p);
|
||||||
|
p = nxt;
|
||||||
|
}
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees a IRfunction and all its components.
|
||||||
|
void IRfunction_free(struct IRfunction *self) {
|
||||||
|
if (self->name) {
|
||||||
|
free(self->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct llist_node *p = self->bs.head, *nxt;
|
||||||
|
while (p) {
|
||||||
|
nxt = p->nxt;
|
||||||
|
IRblock_free((void*)p);
|
||||||
|
p = nxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs the instruction.
|
||||||
|
void IRinstruction_print(struct IRinstruction *self, FILE *Outfile) {
|
||||||
|
switch(self->op) {
|
||||||
|
case IR_IMM_I32: {
|
||||||
|
fprintf(Outfile, "\t$%d = i32 %d;\n", self->id, self->val_i32);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case IR_RET: {
|
||||||
|
if (self->left) {
|
||||||
|
fprintf(Outfile, "\tret $%d.\n", self->left->id);
|
||||||
|
} else {
|
||||||
|
fputs("\tret.", Outfile);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case IR_NEG: {
|
||||||
|
fprintf(Outfile, "\t$%d = neg $%d;\n", self->id, self->left->id);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case IR_NOT: {
|
||||||
|
fprintf(Outfile, "\t$%d = not $%d;\n", self->id, self->left->id);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case IR_CMP_EQ: {
|
||||||
|
fprintf(Outfile, "\t$%d = eq $%d, $%d;\n", self->id, self->left->id, self->right->id);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
fail_ir_op(self->op, __FUNCTION__);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs the containing instructions of the IRblock.
|
||||||
|
void IRblock_print(struct IRblock *self, FILE *Outfile) {
|
||||||
|
fprintf(Outfile, "L%d:\n", self->id);
|
||||||
|
struct llist_node *p = self->ins.head;
|
||||||
|
while (p) {
|
||||||
|
IRinstruction_print((void*)p, Outfile);
|
||||||
|
p = p->nxt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs the containing instructions of the IRfunction.
|
||||||
|
void IRfunction_print(struct IRfunction *self, FILE *Outfile) {
|
||||||
|
fprintf(Outfile, "%s:\n", self->name);
|
||||||
|
struct llist_node *p = self->bs.head;
|
||||||
|
while (p) {
|
||||||
|
IRblock_print((void*)p, Outfile);
|
||||||
|
p = p->nxt;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "fatals.h"
|
#include "fatals.h"
|
||||||
|
#include "util/misc.h"
|
||||||
#include "util/linklist.h"
|
#include "util/linklist.h"
|
||||||
|
|
||||||
const char *ast_opname[] = {
|
const char *ast_opname[] = {
|
||||||
@ -105,6 +106,7 @@ static void ast_print_dfs(FILE* Outfile, struct ASTnode *x, int tabs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch(x->op) {
|
switch(x->op) {
|
||||||
|
case A_LNOT: case A_BNOT: case A_NEG:
|
||||||
case A_RETURN: case A_PRINT: {
|
case A_RETURN: case A_PRINT: {
|
||||||
struct ASTunnode *t = (struct ASTunnode*)x;
|
struct ASTunnode *t = (struct ASTunnode*)x;
|
||||||
fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]);
|
fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]);
|
||||||
@ -179,7 +181,7 @@ void ast_free(struct ASTnode *x) {
|
|||||||
switch (x->op) {
|
switch (x->op) {
|
||||||
case A_IF: {
|
case A_IF: {
|
||||||
ast_free(((struct ASTifnode*)x)->cond);
|
ast_free(((struct ASTifnode*)x)->cond);
|
||||||
} [[fallthrough]];
|
} // fall through
|
||||||
|
|
||||||
case A_ASSIGN:
|
case A_ASSIGN:
|
||||||
case A_ADD: case A_SUB: case A_MUL: case A_DIV:
|
case A_ADD: case A_SUB: case A_MUL: case A_DIV:
|
||||||
|
21
src/fatals.c
21
src/fatals.c
@ -2,7 +2,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "scan.h"
|
#include "scan.h"
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "quad.h"
|
#include "acir.h"
|
||||||
|
|
||||||
void fail_target(const char *target_name) {
|
void fail_target(const char *target_name) {
|
||||||
fprintf(stderr, "unknown target: %s.\n", target_name);
|
fprintf(stderr, "unknown target: %s.\n", target_name);
|
||||||
@ -14,22 +14,13 @@ void fail_malloc(const char *func_name) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* malloc_or_fail(size_t s, const char *func_name) {
|
void fail_ir_op(int op, const char *func_name) {
|
||||||
void *res = malloc(s);
|
if (op < IR_NULL) {
|
||||||
if (res == NULL) {
|
fprintf(stderr, "%s: unsupported IR operator %s.\n", func_name, ast_opname[op]);
|
||||||
fail_malloc(func_name);
|
|
||||||
}
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail_quad_op(int op, const char *func_name) {
|
|
||||||
if (op < Q_NULL) {
|
|
||||||
fprintf(stderr, "%s: unsupported Quad operator %s.\n", func_name, ast_opname[op]);
|
|
||||||
exit(1);
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "%s: unknown Quad operator %d.\n", func_name, op);
|
fprintf(stderr, "%s: unknown IR operator %d.\n", func_name, op);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fail_ast_op(int op, const char *func_name) {
|
void fail_ast_op(int op, const char *func_name) {
|
||||||
|
282
src/parse.c
282
src/parse.c
@ -7,9 +7,13 @@
|
|||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "fatals.h"
|
#include "fatals.h"
|
||||||
|
|
||||||
static struct linklist Tokens; // current token for parsing
|
// Parsing Context
|
||||||
|
struct Pcontext {
|
||||||
|
struct linklist tokens; // token list
|
||||||
|
struct llist_node *cur; // current token
|
||||||
|
};
|
||||||
|
|
||||||
// Check that we have a binary operator and return its precedence.
|
// Checks that we have a binary operator and return its precedence.
|
||||||
// Operators with larger precedence value will be evaluated first.
|
// Operators with larger precedence value will be evaluated first.
|
||||||
static int op_precedence(struct token *t) {
|
static int op_precedence(struct token *t) {
|
||||||
switch (t->type) {
|
switch (t->type) {
|
||||||
@ -86,76 +90,70 @@ static bool direction_rtl(int t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next token
|
// Next token
|
||||||
static void next(void) {
|
static void next(struct Pcontext *ctx) {
|
||||||
if (Tokens.head) {
|
if (ctx->cur) {
|
||||||
token_free(llist_popfront(&Tokens));
|
ctx->cur = ctx->cur->nxt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// preview next kth token from input stream
|
|
||||||
static struct token* preview(int k) {
|
|
||||||
if (Tokens.length <= k) {
|
|
||||||
static struct token token_eof = {
|
|
||||||
.type = T_EOF
|
|
||||||
};
|
|
||||||
return (&token_eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct token* res = llist_get(&Tokens, k);
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return current token from input stream
|
// return current token from input stream
|
||||||
static struct token* current(void) {
|
static struct token* current(struct Pcontext *ctx) {
|
||||||
return (preview(0));
|
static struct token token_eof = {
|
||||||
|
.type = T_EOF
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ctx->cur) {
|
||||||
|
return ((void*)ctx->cur);
|
||||||
|
}
|
||||||
|
return (&token_eof);
|
||||||
}
|
}
|
||||||
|
|
||||||
// match a token or report syntax error
|
// match a token or report syntax error
|
||||||
static void match(int t) {
|
static void match(struct Pcontext *ctx, int t) {
|
||||||
if (current()->type == t) {
|
if (current(ctx)->type == t) {
|
||||||
next();
|
next(ctx);
|
||||||
} else {
|
} else {
|
||||||
fail_ce_expect(current()->line, token_typename[t], token_typename[current()->type]);
|
fail_ce_expect(current(ctx)->line, token_typename[t], token_typename[current(ctx)->type]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check current token's type or report syntax error.
|
// check current token's type or report syntax error.
|
||||||
static void expect(int t) {
|
static void expect(struct Pcontext *ctx, int t) {
|
||||||
if (current()->type != t) {
|
if (current(ctx)->type != t) {
|
||||||
fail_ce_expect(current()->line, token_typename[t], token_typename[current()->type]);
|
fail_ce_expect(current(ctx)->line, token_typename[t], token_typename[current(ctx)->type]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ASTnode* statement(void);
|
static struct ASTnode* statement(struct Pcontext *ctx);
|
||||||
static struct ASTnode* expression(void);
|
static struct ASTnode* expression(struct Pcontext *ctx);
|
||||||
|
|
||||||
// Parse a primary factor and return an
|
// Parse a primary factor and return an
|
||||||
// AST node representing it.
|
// AST node representing it.
|
||||||
static struct ASTnode* primary(void) {
|
static struct ASTnode* primary(struct Pcontext *ctx) {
|
||||||
struct ASTnode *res;
|
struct ASTnode *res;
|
||||||
struct token *t = current();
|
struct token *t = current(ctx);
|
||||||
|
|
||||||
if (t->type == T_LP) {
|
if (t->type == T_LP) {
|
||||||
// ( expr ) considered as primary
|
// ( expr ) considered as primary
|
||||||
next();
|
next(ctx);
|
||||||
res = expression();
|
res = expression(ctx);
|
||||||
match(T_RP);
|
match(ctx, T_RP);
|
||||||
} else if (t->type == T_I32_LIT) {
|
} else if (t->type == T_I32_LIT) {
|
||||||
res = ast_make_lit_i32(t->val_i32);
|
res = ast_make_lit_i32(t->val_i32);
|
||||||
next();
|
next(ctx);
|
||||||
} else if (t->type == T_I64_LIT) {
|
} else if (t->type == T_I64_LIT) {
|
||||||
res = ast_make_lit_i64(current()->val_i64);
|
res = ast_make_lit_i64(current(ctx)->val_i64);
|
||||||
next();
|
next(ctx);
|
||||||
} else if (t->type == T_ID) {
|
} else if (t->type == T_ID) {
|
||||||
// TODO: identifier.
|
// TODO: identifier.
|
||||||
fail_ce(t->line, "got an identifier");
|
fail_ce(t->line, "got an identifier");
|
||||||
/*
|
/*
|
||||||
int id = findglob((char*)current()->val);
|
int id = findglob((char*)current(ctx)->val);
|
||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
fprintf(stderr, "syntax error on line %d: unknown indentifier %s.\n", Line, (char*)current()->val);
|
fprintf(stderr, "syntax error on line %d: unknown indentifier %s.\n", Line, (char*)current(ctx)->val);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
next();
|
next(ctx);
|
||||||
return (ast_make_var(id));
|
return (ast_make_var(id));
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
@ -176,16 +174,16 @@ static bool is_prefix_op(int op) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parses a primary expression with prefixes, e.g. ~10
|
// Parses a primary expression with prefixes, e.g. ~10
|
||||||
static struct ASTnode* prefixed_primary(void) {
|
static struct ASTnode* prefixed_primary(struct Pcontext *ctx) {
|
||||||
struct token *t = current();
|
struct token *t = current(ctx);
|
||||||
|
|
||||||
if (is_prefix_op(t->type)) {
|
if (is_prefix_op(t->type)) {
|
||||||
next();
|
next(ctx);
|
||||||
struct ASTnode *child = prefixed_primary();
|
struct ASTnode *child = prefixed_primary(ctx);
|
||||||
return (ast_make_unary(unary_arithop(t), child));
|
return (ast_make_unary(unary_arithop(t), child));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (primary());
|
return (primary(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the given token type can be a binary operator.
|
// Returns whether the given token type can be a binary operator.
|
||||||
@ -204,28 +202,28 @@ static bool is_binop(int t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return an AST tree whose root is a binary operator
|
// Return an AST tree whose root is a binary operator
|
||||||
static struct ASTnode* binexpr(int precedence) {
|
static struct ASTnode* binexpr(struct Pcontext *ctx, int precedence) {
|
||||||
struct ASTnode *left, *right;
|
struct ASTnode *left, *right;
|
||||||
|
|
||||||
left = prefixed_primary();
|
left = prefixed_primary(ctx);
|
||||||
struct token *op = current();
|
struct token *op = current(ctx);
|
||||||
if (!is_binop(op->type)) {
|
if (!is_binop(op->type)) {
|
||||||
return (left);
|
return (left);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tp = op_precedence(op);
|
int tp = op_precedence(op);
|
||||||
while (tp > precedence) {
|
while (tp > precedence) {
|
||||||
next();
|
next(ctx);
|
||||||
|
|
||||||
if (direction_rtl(op->type)) {
|
if (direction_rtl(op->type)) {
|
||||||
right = binexpr(precedence);
|
right = binexpr(ctx, precedence);
|
||||||
left = ast_make_assign(binary_arithop(op), left, right);
|
left = ast_make_assign(binary_arithop(op), left, right);
|
||||||
} else {
|
} else {
|
||||||
right = binexpr(tp);
|
right = binexpr(ctx, tp);
|
||||||
left = ast_make_binary(binary_arithop(op), left, right); // join right into left
|
left = ast_make_binary(binary_arithop(op), left, right); // join right into left
|
||||||
}
|
}
|
||||||
|
|
||||||
op = current();
|
op = current(ctx);
|
||||||
if (!is_binop(op->type)) {
|
if (!is_binop(op->type)) {
|
||||||
return (left);
|
return (left);
|
||||||
}
|
}
|
||||||
@ -235,70 +233,70 @@ static struct ASTnode* binexpr(int precedence) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse one block of code, e.g. { a; b; }
|
// parse one block of code, e.g. { a; b; }
|
||||||
static struct ASTnode* block(void) {
|
static struct ASTnode* block(struct Pcontext *ctx) {
|
||||||
match(T_LB);
|
match(ctx, T_LB);
|
||||||
if (current()->type == T_RB) {
|
if (current(ctx)->type == T_RB) {
|
||||||
next();
|
next(ctx);
|
||||||
return NULL;
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ASTblocknode* res = (struct ASTblocknode*)ast_make_block();
|
struct ASTblocknode* res = (struct ASTblocknode*)ast_make_block();
|
||||||
while (current()->type != T_RB) {
|
while (current(ctx)->type != T_RB) {
|
||||||
struct ASTnode *x;
|
struct ASTnode *x;
|
||||||
x = statement();
|
x = statement(ctx);
|
||||||
llist_pushback_notnull(&res->st, x);
|
llist_pushback_notnull(&res->st, x);
|
||||||
|
|
||||||
if (current()->type == T_EOF) {
|
if (current(ctx)->type == T_EOF) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match(T_RB);
|
match(ctx, T_RB);
|
||||||
return ((struct ASTnode*)res);
|
return ((struct ASTnode*)res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse an expression
|
// parse an expression
|
||||||
static struct ASTnode* expression(void) {
|
static struct ASTnode* expression(struct Pcontext *ctx) {
|
||||||
if (current()->type == T_SEMI) {
|
if (current(ctx)->type == T_SEMI) {
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (binexpr(0));
|
return (binexpr(ctx, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse one print statement
|
// parse one print statement
|
||||||
static struct ASTnode* print_statement(void) {
|
static struct ASTnode* print_statement(struct Pcontext *ctx) {
|
||||||
match(T_PRINT);
|
match(ctx, T_PRINT);
|
||||||
struct ASTnode *res = ast_make_unary(A_PRINT, expression());
|
struct ASTnode *res = ast_make_unary(A_PRINT, expression(ctx));
|
||||||
match(T_SEMI);
|
match(ctx, T_SEMI);
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// parse variable declaration statement
|
// parse variable declaration statement
|
||||||
static struct ASTnode* var_declaration(void) {
|
static struct ASTnode* var_declaration(void) {
|
||||||
match(T_INT);
|
match(ctx, T_INT);
|
||||||
expect(T_IDENT);
|
expect(ctx, T_IDENT);
|
||||||
if (findglob((char*)current()->val) != -1) {
|
if (findglob((char*)current(ctx)->val) != -1) {
|
||||||
fail_ce("variable declared twice.");
|
fail_ce("variable declared twice.");
|
||||||
}
|
}
|
||||||
addglob((char*)current()->val);
|
addglob((char*)current(ctx)->val);
|
||||||
next();
|
next(ctx);
|
||||||
match(T_SEMI);
|
match(ctx, T_SEMI);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// parse an if statement
|
// parse an if statement
|
||||||
static struct ASTnode* if_statement(void) {
|
static struct ASTnode* if_statement(struct Pcontext *ctx) {
|
||||||
match(T_IF); // if
|
match(ctx, T_IF); // if
|
||||||
match(T_LP); // (
|
match(ctx, T_LP); // (
|
||||||
struct ASTnode* cond = expression();
|
struct ASTnode* cond = expression(ctx);
|
||||||
match(T_RP); // )
|
match(ctx, T_RP); // )
|
||||||
struct ASTnode* then = statement();
|
struct ASTnode* then = statement(ctx);
|
||||||
struct ASTnode* else_then;
|
struct ASTnode* else_then;
|
||||||
if (current()->type == T_ELSE) {
|
if (current(ctx)->type == T_ELSE) {
|
||||||
next(); // else
|
next(ctx); // else
|
||||||
else_then = statement();
|
else_then = statement(ctx);
|
||||||
} else {
|
} else {
|
||||||
else_then = NULL; // empty block
|
else_then = NULL; // empty block
|
||||||
}
|
}
|
||||||
@ -306,39 +304,39 @@ static struct ASTnode* if_statement(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse an while statement
|
// parse an while statement
|
||||||
static struct ASTnode* while_statement(void) {
|
static struct ASTnode* while_statement(struct Pcontext *ctx) {
|
||||||
match(T_WHILE);
|
match(ctx, T_WHILE);
|
||||||
match(T_LP);
|
match(ctx, T_LP);
|
||||||
struct ASTnode* cond = expression();
|
struct ASTnode* cond = expression(ctx);
|
||||||
match(T_RP);
|
match(ctx, T_RP);
|
||||||
struct ASTnode* body = statement();
|
struct ASTnode* body = statement(ctx);
|
||||||
return (ast_make_binary(A_WHILE, cond, body));
|
return (ast_make_binary(A_WHILE, cond, body));
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse a for statement (into a while loop)
|
// parse a for statement (into a while loop)
|
||||||
static struct ASTnode* for_statement(void) {
|
static struct ASTnode* for_statement(struct Pcontext *ctx) {
|
||||||
match(T_FOR);
|
match(ctx, T_FOR);
|
||||||
match(T_LP);
|
match(ctx, T_LP);
|
||||||
struct ASTnode *init = statement();
|
struct ASTnode *init = statement(ctx);
|
||||||
|
|
||||||
struct ASTnode *cond;
|
struct ASTnode *cond;
|
||||||
if (current()->type != T_SEMI) {
|
if (current(ctx)->type != T_SEMI) {
|
||||||
cond = expression();
|
cond = expression(ctx);
|
||||||
} else {
|
} else {
|
||||||
cond = ast_make_lit_i32(1);
|
cond = ast_make_lit_i32(1);
|
||||||
}
|
}
|
||||||
next(); // skip the ;
|
next(ctx); // skip the ;
|
||||||
|
|
||||||
struct ASTnode *inc;
|
struct ASTnode *inc;
|
||||||
if (current()->type != T_RP) {
|
if (current(ctx)->type != T_RP) {
|
||||||
inc = expression();
|
inc = expression(ctx);
|
||||||
} else {
|
} else {
|
||||||
inc = NULL;
|
inc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
match(T_RP);
|
match(ctx, T_RP);
|
||||||
struct ASTnode *body = statement();
|
struct ASTnode *body = statement(ctx);
|
||||||
struct ASTblocknode *container = (struct ASTblocknode*)ast_make_block();
|
struct ASTblocknode *container = (void*)ast_make_block();
|
||||||
struct ASTnode *wbody;
|
struct ASTnode *wbody;
|
||||||
|
|
||||||
if (body == NULL && inc == NULL) {
|
if (body == NULL && inc == NULL) {
|
||||||
@ -348,53 +346,53 @@ static struct ASTnode* for_statement(void) {
|
|||||||
} else if (inc == NULL) {
|
} else if (inc == NULL) {
|
||||||
wbody = body;
|
wbody = body;
|
||||||
} else {
|
} else {
|
||||||
struct ASTblocknode* wt = (struct ASTblocknode*)ast_make_block();
|
struct ASTblocknode* wt = (void*)ast_make_block();
|
||||||
llist_pushback_notnull(&wt->st, body);
|
llist_pushback_notnull(&wt->st, body);
|
||||||
llist_pushback_notnull(&wt->st, inc);
|
llist_pushback_notnull(&wt->st, inc);
|
||||||
wbody = (struct ASTnode*)wt;
|
wbody = (void*)wt;
|
||||||
}
|
}
|
||||||
|
|
||||||
llist_pushback_notnull(&container->st, init);
|
llist_pushback_notnull(&container->st, init);
|
||||||
llist_pushback(&container->st, ast_make_binary(A_WHILE, cond, wbody));
|
llist_pushback(&container->st, ast_make_binary(A_WHILE, cond, wbody));
|
||||||
return ((struct ASTnode*)container);
|
return ((void*)container);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ASTnode* return_statement(void) {
|
static struct ASTnode* return_statement(struct Pcontext *ctx) {
|
||||||
match(T_RETURN);
|
match(ctx, T_RETURN);
|
||||||
struct ASTnode *res = expression();
|
struct ASTnode *res = expression(ctx);
|
||||||
match(T_SEMI);
|
match(ctx, T_SEMI);
|
||||||
return (ast_make_unary(A_RETURN, res));
|
return (ast_make_unary(A_RETURN, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse one statement
|
// parse one statement
|
||||||
static struct ASTnode* statement(void) {
|
static struct ASTnode* statement(struct Pcontext *ctx) {
|
||||||
switch (current()->type) {
|
switch (current(ctx)->type) {
|
||||||
case T_LB:
|
case T_LB:
|
||||||
return (block());
|
return (block(ctx));
|
||||||
|
|
||||||
case T_SEMI:
|
case T_SEMI:
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
case T_PRINT:
|
case T_PRINT:
|
||||||
return (print_statement());
|
return (print_statement(ctx));
|
||||||
|
|
||||||
// case T_INT:
|
// case T_INT:
|
||||||
// return (var_declaration());
|
// return (var_declaration());
|
||||||
case T_IF:
|
case T_IF:
|
||||||
return (if_statement());
|
return (if_statement(ctx));
|
||||||
|
|
||||||
case T_WHILE:
|
case T_WHILE:
|
||||||
return (while_statement());
|
return (while_statement(ctx));
|
||||||
|
|
||||||
case T_FOR:
|
case T_FOR:
|
||||||
return (for_statement());
|
return (for_statement(ctx));
|
||||||
|
|
||||||
case T_RETURN:
|
case T_RETURN:
|
||||||
return (return_statement());
|
return (return_statement(ctx));
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
struct ASTnode* res = expression();
|
struct ASTnode* res = expression(ctx);
|
||||||
match(T_SEMI);
|
match(ctx, T_SEMI);
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,36 +400,46 @@ static struct ASTnode* statement(void) {
|
|||||||
|
|
||||||
// Parse one top-level function
|
// Parse one top-level function
|
||||||
// Sets the func_name param.
|
// Sets the func_name param.
|
||||||
static struct Afunction* function(void) {
|
static struct Afunction* function(struct Pcontext *ctx) {
|
||||||
struct Afunction *res = afunc_make();
|
struct Afunction *res = afunc_make();
|
||||||
|
|
||||||
match(T_INT);
|
match(ctx, T_INT);
|
||||||
expect(T_ID);
|
expect(ctx, T_ID);
|
||||||
res->name = current()->val_s; // transfer ownership of the identifier string to caller
|
res->name = current(ctx)->val_s; // transfer ownership of the identifier string to caller
|
||||||
current()->val_s = NULL; // prevent it from being freed in token_free() called by next().
|
current(ctx)->val_s = NULL; // prevent it from being freed in token_free() called by next(ctx).
|
||||||
next();
|
next(ctx);
|
||||||
|
|
||||||
match(T_LP);
|
match(ctx, T_LP);
|
||||||
if (current()->type == T_VOID) {
|
if (current(ctx)->type == T_VOID) {
|
||||||
next();
|
next(ctx);
|
||||||
goto END_PARAM_LIST;
|
goto END_PARAM_LIST;
|
||||||
}
|
}
|
||||||
// TODO: parameter list
|
// TODO: parameter list
|
||||||
|
|
||||||
END_PARAM_LIST:
|
END_PARAM_LIST:
|
||||||
match(T_RP);
|
match(ctx, T_RP);
|
||||||
res->rt = block();
|
res->rt = block(ctx);
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse ans return the full ast
|
// Frees a Pcontext and all its components.
|
||||||
struct Afunction* parse_source(const char *filename) {
|
static void Pcontext_free(struct Pcontext *ctx) {
|
||||||
Tokens = scan_tokens(filename);
|
struct llist_node *p = ctx->tokens.head, *nxt;
|
||||||
struct Afunction* res = function();
|
while (p) {
|
||||||
|
nxt = p->nxt;
|
||||||
while (Tokens.length > 0) {
|
token_free((void*)p);
|
||||||
token_free(llist_popfront(&Tokens));
|
p = nxt;
|
||||||
}
|
}
|
||||||
llist_free(&Tokens);
|
}
|
||||||
|
|
||||||
|
// Parse source into AST.
|
||||||
|
struct Afunction* Afunction_from_source(const char *filename) {
|
||||||
|
struct Pcontext ctx = {
|
||||||
|
.tokens = scan_tokens(filename),
|
||||||
|
};
|
||||||
|
ctx.cur = ctx.tokens.head;
|
||||||
|
|
||||||
|
struct Afunction* res = function(&ctx);
|
||||||
|
Pcontext_free(&ctx);
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
240
src/quad.c
240
src/quad.c
@ -1,240 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "quad.h"
|
|
||||||
#include "fatals.h"
|
|
||||||
|
|
||||||
// Constructs a Quad with an operator, a destination and two operands.
|
|
||||||
struct Quad* quad_make(int op, struct Qvar *dest, struct Qvar *left, struct Qvar *right) {
|
|
||||||
struct Quad *x = malloc_or_fail(sizeof(struct Quad), __FUNCTION__);
|
|
||||||
|
|
||||||
x->op = op;
|
|
||||||
x->dest = dest;
|
|
||||||
x->left = left;
|
|
||||||
x->right = right;
|
|
||||||
return (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructs a Quad with a loads an integer immediate (32bits).
|
|
||||||
struct Quad* quad_make_i32(struct Qvar *dest, int32_t v) {
|
|
||||||
struct Quad *x = malloc_or_fail(sizeof(struct Quad), __FUNCTION__);
|
|
||||||
|
|
||||||
x->op = Q_IMM_I32;
|
|
||||||
x->dest = dest;
|
|
||||||
x->val_i32 = v;
|
|
||||||
return (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructs a Quad with instruction Q_BR_XXX (conditional goto).
|
|
||||||
struct Quad* quad_make_br(int op, struct Qblock *bt, struct Qblock *bf) {
|
|
||||||
struct Quad *x = malloc_or_fail(sizeof(struct Quad), __FUNCTION__);
|
|
||||||
|
|
||||||
x->op = op;
|
|
||||||
x->bt = bt;
|
|
||||||
x->bf = bf;
|
|
||||||
return (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns whether the given opcode is a opcode from a terminate.
|
|
||||||
bool quad_is_terminate(int op) {
|
|
||||||
switch (op) {
|
|
||||||
case Q_RET:
|
|
||||||
case Q_BR_ALWAYS:
|
|
||||||
return (true);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructs a Qblock.
|
|
||||||
struct Qblock* qblock_make(int id) {
|
|
||||||
struct Qblock *x = malloc_or_fail(sizeof(struct Qblock), __FUNCTION__);
|
|
||||||
llist_init(&x->ins);
|
|
||||||
x->id = id;
|
|
||||||
x->is_complete = false;
|
|
||||||
return (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocates a new basic block in the give function.
|
|
||||||
struct Qblock* qfunc_new_block(struct Qfunction *f) {
|
|
||||||
struct Qblock *x = qblock_make(f->bs.length);
|
|
||||||
llist_pushback(&f->bs, x);
|
|
||||||
return (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appends an instruction to a block.
|
|
||||||
void qblock_add_ins(struct Qblock *b, struct Quad *x) {
|
|
||||||
if (b->is_complete) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
llist_pushback(&b->ins, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructs a new Qvar.
|
|
||||||
struct Qvar* qvar_make(int id) {
|
|
||||||
struct Qvar *res = malloc_or_fail(sizeof(struct Qvar), __FUNCTION__);
|
|
||||||
res->id = id;
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocates a new variable in the given function.
|
|
||||||
struct Qvar* qfunc_new_var(struct Qfunction *f) {
|
|
||||||
struct Qvar *res = qvar_make(f->vars.length);
|
|
||||||
llist_pushback(&f->vars, res);
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate an AST unary arithmetic opcode to a Quad opcode.
|
|
||||||
static int unary_arithop(int op) {
|
|
||||||
switch (op) {
|
|
||||||
case A_NEG: return (Q_NEG);
|
|
||||||
case A_BNOT: return (Q_NOT);
|
|
||||||
default: fail_quad_op(op, __FUNCTION__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct Qvar* qcg_dfs(struct ASTnode *x, struct Qfunction *f, struct Qblock *b) {
|
|
||||||
if (x == NULL) {
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (x->op) {
|
|
||||||
case A_RETURN: {
|
|
||||||
struct ASTunnode *t = (void*)x;
|
|
||||||
struct Qvar *value = qcg_dfs(t->left, f, b);
|
|
||||||
qblock_add_ins(b, quad_make(Q_RET, NULL, value, NULL));
|
|
||||||
b->is_complete = true;
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
case A_BLOCK: {
|
|
||||||
struct ASTblocknode *t = (void*)x;
|
|
||||||
struct llist_node *p = t->st.head;
|
|
||||||
while (p) {
|
|
||||||
qcg_dfs((struct ASTnode*)p, f, b);
|
|
||||||
p = p->nxt;
|
|
||||||
}
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
case A_LIT_I32: {
|
|
||||||
struct Qvar *res = qfunc_new_var(f);
|
|
||||||
struct ASTi32node *t = (void*)x;
|
|
||||||
qblock_add_ins(b, quad_make_i32(res, t->val));
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
case A_NEG: case A_BNOT: {
|
|
||||||
struct Qvar *res = qfunc_new_var(f);
|
|
||||||
struct ASTunnode *t = (void*)x;
|
|
||||||
struct Qvar *value = qcg_dfs(t->left, f, b);
|
|
||||||
qblock_add_ins(b, quad_make(unary_arithop(x->op), res, value, NULL));
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
case A_LNOT: {
|
|
||||||
struct Qvar *res = qfunc_new_var(f);
|
|
||||||
struct ASTunnode *t = (void*)x;
|
|
||||||
struct Qvar *value = qcg_dfs(t->left, f, b);
|
|
||||||
struct Qvar *zero = qfunc_new_var(f);
|
|
||||||
qblock_add_ins(b, quad_make_i32(zero, 0));
|
|
||||||
qblock_add_ins(b, quad_make(Q_CMP_EQ, res, value, zero));
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
fail_ast_op(x->op, __FUNCTION__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates Quad Repersentation from an AST
|
|
||||||
struct Qfunction* qfunc_cgenerate(struct Afunction *afunc) {
|
|
||||||
struct Qfunction *self = malloc_or_fail(sizeof(struct Qfunction), __FUNCTION__);
|
|
||||||
|
|
||||||
self->name = afunc->name; // transfer ownership of function name string
|
|
||||||
afunc->name = NULL; // prevents the pointer being freed when freeing the Afunction
|
|
||||||
|
|
||||||
llist_init(&self->bs);
|
|
||||||
llist_init(&self->vars);
|
|
||||||
|
|
||||||
struct Qblock *entry = qfunc_new_block(self);
|
|
||||||
qcg_dfs(afunc->rt, self, entry);
|
|
||||||
return (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frees a Qblock and all its components.
|
|
||||||
void qblock_free(struct Qblock *b) {
|
|
||||||
llist_free(&b->ins);
|
|
||||||
free(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frees a Qfunction and all its components.
|
|
||||||
void qfunc_free(struct Qfunction *f) {
|
|
||||||
if (f->name) {
|
|
||||||
free(f->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct llist_node *p = f->bs.head, *nxt;
|
|
||||||
while (p) {
|
|
||||||
nxt = p->nxt;
|
|
||||||
qblock_free((struct Qblock*)p);
|
|
||||||
p = nxt;
|
|
||||||
}
|
|
||||||
|
|
||||||
llist_free(&f->vars);
|
|
||||||
free(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prints the given instruction for debugging.
|
|
||||||
static void quad_debug_print(struct Quad *self, FILE *Outfile) {
|
|
||||||
switch(self->op) {
|
|
||||||
case Q_IMM_I32: {
|
|
||||||
fprintf(Outfile, "\t$%d = i32 %d;\n", self->dest->id, self->val_i32);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Q_RET: {
|
|
||||||
if (self->left) {
|
|
||||||
fprintf(Outfile, "\tret $%d.\n", self->left->id);
|
|
||||||
} else {
|
|
||||||
fputs("\tret.", Outfile);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Q_NEG: {
|
|
||||||
fprintf(Outfile, "\t$%d = neg $%d;\n", self->dest->id, self->left->id);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Q_NOT: {
|
|
||||||
fprintf(Outfile, "\t$%d = not $%d;\n", self->dest->id, self->left->id);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Q_CMP_EQ: {
|
|
||||||
fprintf(Outfile, "\t$%d = eq $%d, $%d;\n", self->dest->id, self->left->id, self->right->id);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: {
|
|
||||||
fail_quad_op(self->op, __FUNCTION__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prints the contents of a Qblock for debugging.
|
|
||||||
static void qblock_debug_print(struct Qblock *self, FILE *Outfile) {
|
|
||||||
fprintf(Outfile, "L%d:\n", self->id);
|
|
||||||
struct llist_node *p = self->ins.head;
|
|
||||||
while (p) {
|
|
||||||
quad_debug_print((struct Quad*)p, Outfile);
|
|
||||||
p = p->nxt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prints the contents of a Qfunction for debugging.
|
|
||||||
void qfunc_debug_print(struct Qfunction *self, FILE *Outfile) {
|
|
||||||
fprintf(Outfile, "%s:\n", self->name);
|
|
||||||
struct llist_node *p = self->bs.head;
|
|
||||||
while (p) {
|
|
||||||
qblock_debug_print((struct Qblock*)p, Outfile);
|
|
||||||
p = p->nxt;
|
|
||||||
}
|
|
||||||
}
|
|
@ -71,7 +71,7 @@ static char* scan_indentifier(int *n) {
|
|||||||
while (isdigit(c) || isalpha(c) || c == '_') {
|
while (isdigit(c) || isalpha(c) || c == '_') {
|
||||||
if (len >= sz - 1) {
|
if (len >= sz - 1) {
|
||||||
sz *= 2;
|
sz *= 2;
|
||||||
char *res = realloc(res, sz * sizeof(char));
|
res = realloc(res, sz * sizeof(char));
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
fail_malloc(__FUNCTION__);
|
fail_malloc(__FUNCTION__);
|
||||||
}
|
}
|
||||||
|
123
src/symbol.c
123
src/symbol.c
@ -1,123 +0,0 @@
|
|||||||
/*
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "util/misc.h"
|
|
||||||
#include "util/array.h"
|
|
||||||
|
|
||||||
#define INDENT_CHARS 63 // 10 digits + 26 * 2(cap) alpha + 1 _
|
|
||||||
|
|
||||||
static int indent_char_id(char c) {
|
|
||||||
if ('0' <= c && c <= '9') {
|
|
||||||
return (c - '0');
|
|
||||||
}
|
|
||||||
if ('a' <= c && c <= 'z') {
|
|
||||||
return (c - 'a' + 10);
|
|
||||||
}
|
|
||||||
if ('A' <= c && c <= 'Z') {
|
|
||||||
return (c - 'A' + 36);
|
|
||||||
}
|
|
||||||
if (c == '_') {
|
|
||||||
return (62);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "%c is not a acceptable char in indentifier.\n", c);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct trie_node {
|
|
||||||
struct trie_node *c[INDENT_CHARS]; // childs
|
|
||||||
int sz; // size
|
|
||||||
int val; // endpoint value, -1 for not ended
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct trie_node *root;
|
|
||||||
struct array Gsym; // symbol id to name map
|
|
||||||
|
|
||||||
static void trie_free(struct trie_node *p) {
|
|
||||||
for (int i = 0; i < INDENT_CHARS; ++i) {
|
|
||||||
if (p->c[i]) {
|
|
||||||
trie_free(p->c[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct trie_node* trie_createnode(void) {
|
|
||||||
struct trie_node *x = malloc(sizeof(struct trie_node));
|
|
||||||
if (x == NULL) {
|
|
||||||
fprintf(stderr, "%s: failed to malloc trie node in symbol table.\n", __FUNCTION__);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(x, 0, sizeof(struct trie_node));
|
|
||||||
x->val = -1;
|
|
||||||
return (x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void trie_set(char *str, int val) {
|
|
||||||
struct trie_node *p = root;
|
|
||||||
|
|
||||||
int n = strlen(str);
|
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
int x = indent_char_id(str[i]);
|
|
||||||
if (!p->c[x]) {
|
|
||||||
p->c[x] = trie_createnode();
|
|
||||||
}
|
|
||||||
p->sz += 1;
|
|
||||||
p = p->c[x];
|
|
||||||
}
|
|
||||||
p->val = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int trie_get(char *str) {
|
|
||||||
struct trie_node *p = root;
|
|
||||||
|
|
||||||
int n = strlen(str);
|
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
int x = indent_char_id(str[i]);
|
|
||||||
if (!p->c[x]) { // not found
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
p = p->c[x];
|
|
||||||
}
|
|
||||||
return (p->val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int IsSymbolListLoaded = 0;
|
|
||||||
|
|
||||||
// init global symbol table
|
|
||||||
void symbol_init(void) {
|
|
||||||
IsSymbolListLoaded = 1;
|
|
||||||
array_init(&Gsym);
|
|
||||||
root = trie_createnode();
|
|
||||||
}
|
|
||||||
|
|
||||||
// unload global symbol table
|
|
||||||
void symbol_unload(void) {
|
|
||||||
if (!IsSymbolListLoaded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Gsym.length; ++i) {
|
|
||||||
free(array_get(&Gsym, i));
|
|
||||||
}
|
|
||||||
array_free(&Gsym);
|
|
||||||
trie_free(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the symbol s is in the global symbol table.
|
|
||||||
// Return its slot position or -1 if not found.
|
|
||||||
int findglob(char *s) {
|
|
||||||
return (trie_get(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a global symbol to the symbol table.
|
|
||||||
// Return the slot number in the symbol table.
|
|
||||||
int addglob(char *s) {
|
|
||||||
char *ss = strclone(s);
|
|
||||||
array_pushback(&Gsym, ss);
|
|
||||||
int res = Gsym.length - 1;
|
|
||||||
trie_set(ss, res);
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
*/
|
|
@ -6,14 +6,14 @@
|
|||||||
// Parse the target string
|
// Parse the target string
|
||||||
int target_parse(const char *target_string) {
|
int target_parse(const char *target_string) {
|
||||||
static const char *target_map_k[] = {
|
static const char *target_map_k[] = {
|
||||||
"ast",
|
"_ast",
|
||||||
"quad",
|
"_acir",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int target_map_v[] = {
|
static const int target_map_v[] = {
|
||||||
TARGET_AST,
|
TARGET_AST,
|
||||||
TARGET_QUAD,
|
TARGET_ACIR,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; target_map_k[i]; ++i) {
|
for (int i = 0; target_map_k[i]; ++i) {
|
||||||
|
@ -89,8 +89,8 @@ void llist_insert(struct linklist *l, int index, void *val) {
|
|||||||
p->nxt = x;
|
p->nxt = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop the first element of the link list
|
// Pop the first element of the link list.
|
||||||
// Return the first element.
|
// Returns the first element.
|
||||||
void* llist_popfront(struct linklist *l) {
|
void* llist_popfront(struct linklist *l) {
|
||||||
if (l->head == NULL) {
|
if (l->head == NULL) {
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "fatals.h"
|
||||||
#include "util/misc.h"
|
#include "util/misc.h"
|
||||||
|
|
||||||
|
// This function tries to malloc some memory.
|
||||||
|
// Will call fail_malloc() in fatals.h when failing.
|
||||||
|
void* malloc_or_fail(size_t s, const char *func_name) {
|
||||||
|
void *res = malloc(s);
|
||||||
|
if (res == NULL) {
|
||||||
|
fail_malloc(func_name);
|
||||||
|
}
|
||||||
|
return (res);
|
||||||
|
}
|
||||||
|
|
||||||
// This function does what you think it does :)
|
// This function does what you think it does :)
|
||||||
// Returns whether the given strings are the same.
|
// Returns whether the given strings are the same.
|
||||||
bool strequal(const char *s1, const char *s2) {
|
bool strequal(const char *s1, const char *s2) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user