main function(AST)

Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
方而静 2023-06-12 16:02:43 +08:00
parent 6950807161
commit 121c37c16a
53 changed files with 399 additions and 842 deletions

View File

@ -1,33 +1,3 @@
# acc
A zero-dependence (sub) C compiler working in progress written in pure ISO C11.
A (WIP) zero-dependence (sub) C compiler written in pure ISO C11, minimal but aims to implement all 4 passes(lex, parse, build ir, code generation) and a set of machine-independent optimizations.
## Build from source
Make sure you have `gcc` and `make` on your PATH.
If you don't, here's command for ubuntu:
```
sudo apt install build-essential
```
To build, run:
```
make
```
## Usage
```
acc target inputfile (outputfile)
```
Output targets now includes:
- `x86_64`: Intel's x84-64 ASM
- `llvm`: LLVM's IR
- `ast`: (used for debugging) Abstruct Syntax Tree
Example:
```
acc x86_64 test.c
```

View File

@ -9,41 +9,38 @@ enum {
A_ASSIGN,
A_ADD, A_SUB, A_MUL, A_DIV,
A_EQ, A_NE, A_LT, A_GT, A_LE, A_GE,
A_LIT, A_VAR,
A_LIT_I32, A_LIT_I64,
A_VAR,
A_BLOCK,
A_PRINT, A_IF, A_WHILE,
A_RETURN,
A_SOUL // what?
};
// value type
enum {
V_I32, V_I64, V_BOOL
};
extern const char *ast_opname[31];
struct value_type {
int vt; // base value type
};
// AST nodde types
enum {
N_BIN, N_UN, N_MULTI, N_LEAF, N_ASSIGN
};
// AST structure field shared by all types
// llist_node *n : for linklist
// int op : node operation
#define ASTnode_SHARED_FIELDS \
struct llist_node n; \
int op;
// AST structure (common)
struct ASTnode {
int op; //operator
ASTnode_SHARED_FIELDS
};
// AST binary operation node
struct ASTbinnode {
int op;
ASTnode_SHARED_FIELDS
struct ASTnode *left;
struct ASTnode *right;
};
// AST if statement node
struct ASTifnode {
int op;
ASTnode_SHARED_FIELDS
struct ASTnode *left; // condition true branch
struct ASTnode *right; // condition false branch
struct ASTnode *cond;
@ -51,33 +48,38 @@ struct ASTifnode {
// AST unary operation node
struct ASTunnode {
int op;
struct ASTnode *c;
ASTnode_SHARED_FIELDS
struct ASTnode *left;
};
// AST block node
struct ASTblocknode {
int op;
ASTnode_SHARED_FIELDS
struct linklist st; // statements linklist
};
// AST literal node
struct ASTlitnode {
int op;
struct value_type type;
void *val;
// AST integer literal (32bit) node
struct ASTi32node {
ASTnode_SHARED_FIELDS
int32_t val;
};
// AST integer literal (64bit) node
struct ASTi64node {
ASTnode_SHARED_FIELDS
int64_t val;
};
// AST assign literal node
struct ASTassignnode {
int op;
int left;
ASTnode_SHARED_FIELDS
struct ASTnode* left;
struct ASTnode* right;
};
// AST variable value node
struct ASTvarnode {
int op;
ASTnode_SHARED_FIELDS
int id;
};
@ -87,9 +89,8 @@ struct ASTnode* ast_make_lit_i64(int64_t x);
struct ASTnode* ast_make_unary(int op, struct ASTnode *c);
struct ASTnode* ast_make_block();
struct ASTnode* ast_make_var(int id);
struct ASTnode* ast_make_assign(int op, int left, struct ASTnode *right);
struct ASTnode* ast_make_assign(int op, struct ASTnode *left, struct ASTnode *right);
struct ASTnode* ast_make_if(struct ASTnode *left, struct ASTnode *right, struct ASTnode *cond);
int ast_type(int t);
void ast_free(struct ASTnode *x);
#endif

View File

@ -1,29 +0,0 @@
#ifndef ACC_CG_H
#define ACC_CG_H
#include "ast.h"
extern FILE *Outfile;
// cg.c
void cg_main(int target, struct ASTnode *rt);
void open_outputfile(char *filename);
void cg_unload(void);
// cg_x64.c
//void cgx64_generate(struct ASTnode *rt);
// cg_llvm.c
//void cgllvm_generate(struct ASTnode *rt);
// cg_ast.c
void cgast_generate(struct ASTnode *rt);
// targets
enum {
CG_X64, // Intel x86_64
CG_LLVM, // LLVM IR
CG_AST, // Abstruct Syntax Tree
};
#endif

View File

@ -5,6 +5,7 @@
#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_malloc(const char *func_name);
noreturn void fail_ast_op(int op, const char *func_name);
noreturn void fail_ce_expect(const char *expected, const char *got);

7
include/print_ast.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef ACC_DEBUG_PRINT_AST
#define ACC_DEBUG_PRINT_AST
void debug_ast_print(FILE *Outfile, struct ASTnode *rt);
#endif

View File

@ -1,7 +1,7 @@
#ifndef ACC_SCAN_H
#define ACC_SCAN_H
#include "token.h"
#include "util/linklist.h"
extern int Line;
struct linklist scan_tokens(const char *name);

View File

@ -1,3 +1,4 @@
/*
#ifndef ACC_SYMBOL_H
#define ACC_SYMBOL_H
@ -11,3 +12,4 @@ int findglob(char *s);
int addglob(char *s);
#endif
*/

11
include/target.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef ACC_TARGET_H
#define ACC_TARGET_H
// Target types
enum {
TARGET_AST
};
int target_parse(const char *target_string);
#endif

View File

@ -1,29 +1,41 @@
#ifndef ACC_TOKEN_H
#define ACC_TOKEN_H
#include <stdint.h>
#include "util/linklist.h"
// Token structure
struct token {
struct llist_node n;
int type; // token type
void* val; // hold the value of the literal that we scanned in
union { // hold the value of the literal that we scanned in
int16_t val_i16;
int32_t val_i32;
int64_t val_i64;
char *val_s;
};
};
// Tokens
enum {
T_EOF,
T_SEMI,
T_LB, T_RB, T_LP, T_RP,
T_ASSIGN,
T_PLUS, T_MINUS, T_STAR, T_SLASH,
T_EQ, T_NE, T_LT, T_GT, T_LE, T_GE,
T_INT, T_VOID, T_CHAR, T_LONG,
T_PRINT, T_IF, T_ELSE, T_WHILE, T_FOR,
T_I32_LIT, T_I64_LIT, T_INDENT,
T_SEMI, // ;
T_LB, T_RB, T_LP, T_RP, // { } ( )
T_ASSIGN, // =
T_PLUS, T_MINUS, T_STAR, T_SLASH, // + - * /
T_EQ, T_NE, T_LT, T_GT, T_LE, T_GE, // == != < > <= >=
T_INT, T_VOID, T_CHAR, T_LONG, // int void char long
T_SHORT, // short
T_PRINT, T_IF, T_ELSE, // print if else
T_WHILE, T_FOR, // while for
T_RETURN, // return
T_I16_LIT, T_I32_LIT, T_I64_LIT,
T_ID,
T_EXCEED,
};
extern const char *token_typename[63];
void token_free(struct token *t);
struct token token_make_eof(void);
#endif

View File

@ -1,3 +1,4 @@
/*
#ifndef ACC_UTIL_HASHMAP
#define ACC_UTIL_HASHMAP
@ -10,3 +11,4 @@ struct hashmap {
void hashmap_init();
#endif
*/

View File

@ -2,7 +2,6 @@
#define ACC_UTIL_LINKLIST_H
struct llist_node {
void *val;
struct llist_node *nxt;
};
@ -12,8 +11,6 @@ struct linklist {
struct llist_node *tail;
};
struct llist_node* llist_createnode(void *val);
void llist_init(struct linklist *l);
void llist_free(struct linklist *l);
void llist_free_full(struct linklist *l);

43
main.c
View File

@ -3,9 +3,9 @@
#include <string.h>
#include "scan.h"
#include "parse.h"
#include "cg.h"
#include "ast.h"
#include "symbol.h"
#include "target.h"
#include "print_ast.h"
// Print out a usage if started incorrectly
static void usage(char *prog) {
@ -14,48 +14,29 @@ static void usage(char *prog) {
exit(1);
}
//Do clean up job
// Do clean up job
void unload(void) {
cg_unload();
symbol_unload();
}
int main(int argc, char *argv[]) {
atexit(unload);
// atexit(unload);
if (argc < 3) {
usage(argv[0]);
}
int outfile_opened = 0;
FILE *Outfile;
if (argc >= 4) {
open_outputfile(argv[3]);
outfile_opened = 1;
}
int target;
if (!strcmp(argv[1], "x86_64")) {
target = CG_X64;
if (!outfile_opened) {
open_outputfile("out.s");
}
} else if (!strcmp(argv[1], "llvm")) {
target = CG_LLVM;
if (!outfile_opened) {
open_outputfile("out.ll");
}
} else if (!strcmp(argv[1], "ast")) {
target = CG_AST;
if (!outfile_opened) {
open_outputfile("out.txt");
}
Outfile = fopen(argv[3], "w");
} else {
fprintf(stderr, "Unknow target %s.\n", argv[1]);
exit(1);
Outfile = fopen("out.txt", "w");
}
symbol_init();
int target = target_parse(argv[1]);
struct ASTnode *rt = parse(argv[2]);
cg_main(target, rt);
if (target == TARGET_AST) {
debug_ast_print(Outfile, rt);
}
ast_free(rt);
fclose(Outfile);
return (0);
}

100
src/ast.c
View File

@ -4,6 +4,18 @@
#include "fatals.h"
#include "util/linklist.h"
const char *ast_opname[] = {
"=",
"+", "-", "*", "/",
"==", "!=", "<", ">", "<=", ">=",
"int32", "int64",
"var",
"block",
"print", "if", "while",
"return",
NULL
};
// Build and return a binary AST node
struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *right) {
struct ASTbinnode *x = malloc_or_fail(sizeof(struct ASTbinnode), __FUNCTION__);
@ -14,25 +26,21 @@ struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *ri
return ((struct ASTnode*)x);
}
// Make an AST int32 literal node
// Make an AST integer literal (32bit) node
struct ASTnode* ast_make_lit_i32(int32_t v) {
struct ASTlitnode *x = malloc_or_fail(sizeof(struct ASTlitnode), __FUNCTION__);
struct ASTi32node *x = malloc_or_fail(sizeof(struct ASTi32node), __FUNCTION__);
x->op = A_LIT;
x->type.vt = V_I32;
x->val = malloc_or_fail(sizeof(int32_t), __FUNCTION__);
*(int64_t*)x->val = v;
x->op = A_LIT_I32;
x->val = v;
return ((struct ASTnode*)x);
}
// Make an AST int32 literal node
// Make an AST integer literal (64bit) node
struct ASTnode* ast_make_lit_i64(int64_t v) {
struct ASTlitnode *x = malloc_or_fail(sizeof(struct ASTlitnode), __FUNCTION__);
struct ASTi64node *x = malloc_or_fail(sizeof(struct ASTi64node), __FUNCTION__);
x->op = A_LIT;
x->type.vt = V_I64;
x->val = malloc_or_fail(sizeof(int64_t), __FUNCTION__);
*(int64_t*)x->val = v;
x->op = A_LIT_I64;
x->val = v;
return ((struct ASTnode*)x);
}
@ -46,11 +54,11 @@ struct ASTnode* ast_make_var(int id) {
}
// Make a unary AST node: only one child
struct ASTnode* ast_make_unary(int op, struct ASTnode *c) {
struct ASTnode* ast_make_unary(int op, struct ASTnode *child) {
struct ASTunnode *x = malloc_or_fail(sizeof(struct ASTunnode), __FUNCTION__);
x->op = op;
x->c = c;
x->left = child;
return ((struct ASTnode*)x);
}
@ -64,7 +72,7 @@ struct ASTnode* ast_make_block() {
}
// Make a assignment ast node
struct ASTnode* ast_make_assign(int op, int left, struct ASTnode *right) {
struct ASTnode* ast_make_assign(int op, struct ASTnode *left, struct ASTnode *right) {
struct ASTassignnode *x = malloc_or_fail(sizeof(struct ASTassignnode), __FUNCTION__);
x->op = op;
@ -84,65 +92,43 @@ struct ASTnode* ast_make_if(struct ASTnode *left, struct ASTnode *right, struct
return ((struct ASTnode*)x);
}
// Translate ast operation type to ast node type
int ast_type(int t) {
switch (t) {
case A_ADD: case A_SUB: case A_MUL: case A_DIV:
case A_EQ: case A_NE: case A_GT: case A_LT: case A_GE: case A_LE:
case A_IF: case A_WHILE:
return (N_BIN);
case A_ASSIGN:
return (N_ASSIGN);
case A_LIT: case A_VAR:
return (N_LEAF);
case A_BLOCK:
return (N_MULTI);
case A_PRINT:
return (N_UN);
default:
fail_ast_op(t, __FUNCTION__);
}
}
// free an AST's memory
void ast_free(struct ASTnode *x) {
if (x == NULL) {
return;
}
switch (ast_type(x->op)) {
case N_ASSIGN: {
struct ASTassignnode *t = (struct ASTassignnode*)x;
ast_free(t->right);
} break;
case N_BIN: {
switch (x->op) {
case A_IF: {
ast_free(((struct ASTifnode*)x)->cond);
} // dont break
case A_ASSIGN:
case A_ADD: case A_SUB: case A_MUL: case A_DIV:
case A_EQ: case A_NE: case A_GT: case A_LT: case A_GE: case A_LE:
case A_WHILE: {
struct ASTbinnode *t = (struct ASTbinnode*)x;
ast_free(t->left);
ast_free(t->right);
if (x->op == A_IF) {
ast_free(((struct ASTifnode*)x)->cond);
}
} break;
case N_UN: {
case A_PRINT: case A_RETURN: {
struct ASTunnode *t = (struct ASTunnode*)x;
ast_free(t->c);
ast_free(t->left);
} break;
case N_MULTI: {
case A_BLOCK: {
struct ASTblocknode *t = (struct ASTblocknode*)x;
struct llist_node *p = t->st.head;
struct llist_node *p = t->st.head, *nxt;
while (p) {
ast_free(p->val);
p = p->nxt;
nxt = p->nxt;
ast_free(p);
p = nxt;
}
llist_free(&t->st);
} break;
case N_LEAF: {
struct ASTlitnode *t = (struct ASTlitnode*)x;
if (t->op == A_LIT) {
if (t->val) {
free(t->val);
}
}
default: {
} break;
}
free(x);

View File

@ -1,38 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "cg.h"
FILE *Outfile;
// open output file of generated code
void open_outputfile(char *filename) {
Outfile = fopen(filename, "w");
if (Outfile == NULL) {
fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
exit(1);
}
}
// close Outfile at exit.
void cg_unload(void) {
if (Outfile) {
fclose(Outfile);
}
}
// generates code
void cg_main(int target, struct ASTnode *rt) {
if (target == CG_X64) {
//cgx64_generate(rt);
} else if (target == CG_LLVM) {
//cgllvm_generate(rt);
} else if (target == CG_AST) {
cgast_generate(rt);
} else {
fprintf(stderr, "Unknow target %d.\n", target);
exit(1);
}
}

View File

@ -1,119 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "cg.h"
#include "symbol.h"
#include "fatals.h"
#include "util/array.h"
static const char *ast_opname[] = {
"=",
"+", "-", "*", "/",
"==", "!=", "<", ">", "<=", ">=",
"int", "var",
"block",
"print", "if", "while"
};
static int tabs;
static void cgprint_tabs() {
for (int i = 0; i < tabs; ++i) {
fprintf(Outfile, "\t");
}
}
static void cgenerate_dfs(struct ASTnode *x) {
if (x == NULL) {
cgprint_tabs();
fprintf(Outfile, "--->NULL.\n");
return;
}
int nt = ast_type(x->op);
if (nt == N_LEAF) {
if (x->op == A_LIT) {
struct ASTlitnode *t = (struct ASTlitnode*)x;
cgprint_tabs();
switch (t->type.vt) {
case V_I32:
fprintf(Outfile, "--->INT %d.\n", *(int32_t*)t->val);
break;
case V_I64:
fprintf(Outfile, "--->INT64 %lld.\n", *(int64_t*)t->val);
break;
default:
fprintf(stderr, "%s: Unknow literal type %d.\n", __FUNCTION__, t->type.vt);
exit(1);
}
} else if (x->op == A_VAR) {
struct ASTvarnode *t = (struct ASTvarnode*)x;
cgprint_tabs();
fprintf(Outfile, "--->VAR @%s.\n", (char*)array_get(&Gsym, t->id));
} else {
fail_ast_op(x->op, __FUNCTION__);
}
} else if (nt == N_ASSIGN) {
struct ASTassignnode *t = (struct ASTassignnode*)x;
cgprint_tabs();
fprintf(Outfile, "--->ASSIGN(%s) to @%s (right)\n", ast_opname[t->op], (char*)array_get(&Gsym, t->left));
tabs += 1;
cgenerate_dfs(t->right);
tabs -= 1;
} else if (nt == N_BIN) {
if (x->op == A_IF) {
struct ASTifnode *t = (struct ASTifnode*)x;
cgprint_tabs();
fprintf(Outfile, "--->IF (cond left right)\n");
tabs += 1;
cgenerate_dfs(t->cond);
cgenerate_dfs(t->left);
cgenerate_dfs(t->right);
tabs -= 1;
} else if (x->op == A_WHILE) {
struct ASTbinnode *t = (struct ASTbinnode*)x;
cgprint_tabs();
fprintf(Outfile, "--->WHILE(%s) (cond body)\n", ast_opname[t->op]);
tabs += 1;
cgenerate_dfs(t->left);
cgenerate_dfs(t->right);
tabs -= 1;
} else {
struct ASTbinnode *t = (struct ASTbinnode*)x;
cgprint_tabs();
fprintf(Outfile, "--->BINOP(%s) (left right)\n", ast_opname[t->op]);
tabs += 1;
cgenerate_dfs(t->left);
cgenerate_dfs(t->right);
tabs -= 1;
}
} else if (nt == N_UN) {
if (x->op == A_PRINT) {
struct ASTunnode *t = (struct ASTunnode*)x;
cgprint_tabs();
fprintf(Outfile, "--->PRINT (value)\n");
tabs += 1;
cgenerate_dfs(t->c);
tabs -= 1;
} else {
fail_ast_op(x->op, __FUNCTION__);
}
} else if (nt == N_MULTI) {
struct ASTblocknode *t = (struct ASTblocknode*)x;
cgprint_tabs();
fprintf(Outfile, "--->BLOCK (%d childs)\n", t->st.length);
tabs += 1;
struct llist_node *p = t->st.head;
while (p) {
cgenerate_dfs(p->val);
p = p->nxt;
}
tabs -= 1;
}
}
void cgast_generate(struct ASTnode *rt) {
tabs = 0;
cgenerate_dfs(rt);
}

View File

@ -1,216 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "cg.h"
#include "symbol.h"
#include "fatals.h"
#include "util/array.h"
#include "util/linklist.h"
static int alloc_tag(void) {
static int id = 0;
return (id++);
}
static int alloc_label(void) {
static int id = 0;
return (id++);
}
// Print out the ir preamble
static void cgpreamble(void) {
fputs( "@.printint.format = constant [4 x i8] c\"%d\\0A\\00\", align 1\n"
"declare dso_local i32 @printf(i8* readonly nocapture, ...)\n"
"define dso_local void @printint(i32 %0) {\n"
"\t%2 = getelementptr inbounds [4 x i8], [4 x i8]* @.printint.format, i32 0, i32 0\n"
"\t%3 = call i32 (i8*, ...) @printf(i8* %2, i32 %0)\n"
"\tret void\n"
"}\n"
"\n"
"define dso_local i32 @main() {\n"
"entry:\n", Outfile);
}
// Print out the ir postamble
static void cgpostamble(void) {
fputs("\tret i32 0\n}\n", Outfile);
}
// init as global value
static void cginit_glob(char *name) {
fprintf(Outfile, "@%s = dso_local global i32 0, align 4\n", name);
}
// Preform arithmetic operation between two i32
static int cgarith_i32(int x, int y, char *op) {
int r = alloc_tag();
fprintf(Outfile, "\t%%%d = %s i32 %%%d, %%%d\n", r, op, x, y);
return (r);
}
// Preform comparision between integers
static int cgcomp_i(int x, int y, char *op, char *ty) {
int r1 = alloc_tag();
fprintf(Outfile, "\t%%%d = icmp %s %s %%%d, %%%d\n", r1, op, ty, x, y);
int r2 = alloc_tag();
if (strcmp(ty, "i1") != 0) { // special case for bool
fprintf(Outfile, "\t%%%d = zext i1 %%%d to %s\n", r2, r1, ty);
}
return (r2);
}
// Preform comparision between two i32
static int cgcomp_i32(int x, int y, char *op) {
return (cgcomp_i(x, y, op, "i32"));
}
// Load an int literal
static int cgload_lit_i32(int val) {
int r = alloc_tag();
fprintf(Outfile, "\t%%%d = select i1 true, i32 %d, i32 undef\n", r, val);
return (r);
}
// Load an int from a global variable
static int cgload_glob_i32(char *name) {
int r = alloc_tag();
fprintf(Outfile, "\t%%%d = load i32, i32* @%s, align 4\n", r, name);
return (r);
}
// Store an int into a global variable
static int cgstore_glob_i32(int x, char *name) {
fprintf(Outfile, "\tstore i32 %%%d, i32* @%s, align 4\n", x, name);
return (x);
}
// Print a i32
static void cgprint(int x) {
fprintf(Outfile, "\tcall void (i32) @printint(i32 %%%d)\n", x);
}
// Jump to a label no matter what
static void cgjmp_always(int x) {
fprintf(Outfile, "\tbr label %%L%d\n", x);
}
// Conditional jump
static void cgjmp_if_i32(int cond, int Lthen, int Lelse) {
int rc = alloc_tag();
fprintf(Outfile, "\t%%%d = icmp ne i32 0, %%%d\n", rc, cond);
fprintf(Outfile, "\tbr i1 %%%d, label %%L%d, label %%L%d\n", rc, Lthen, Lelse);
}
static void cgprint_label(int L) {
fprintf(Outfile, "L%d:\n", L);
}
// generates llvm ir from ast
static int cgenerate_ast(struct ASTnode *rt) {
int nt = ast_type(rt->op);
if (nt == N_LEAF) {
if (rt->op == A_INTLIT) {
return (cgload_lit_i32(((struct ASTintnode*)rt)->val));
} else if (rt->op == A_VAR) {
return (cgload_glob_i32(array_get(&Gsym, ((struct ASTvarnode*)rt)->id)));
}
fail_ast_op(rt->op, __FUNCTION__);
} else if (nt == N_BIN) {
if (rt->op == A_IF) {
struct ASTifnode *x = (struct ASTifnode*)rt;
int Lthen = alloc_label(), Lelse = alloc_label(), Lend = alloc_label();
int condv = cgenerate_ast(x->cond);
cgjmp_if_i32(condv, Lthen, Lelse);
cgprint_label(Lthen);
cgenerate_ast(x->left);
cgjmp_always(Lend);
cgprint_label(Lelse);
cgenerate_ast(x->right);
cgjmp_always(Lend);
cgprint_label(Lend);
return (-1);
} else if (rt->op == A_WHILE) {
struct ASTbinnode *x = (struct ASTbinnode*)rt;
int Lstart = alloc_label(), Lbody = alloc_label(), Lend = alloc_label();
cgjmp_always(Lstart);
cgprint_label(Lstart);
int condv = cgenerate_ast(x->left);
cgjmp_if_i32(condv, Lbody, Lend);
cgprint_label(Lbody);
cgenerate_ast(x->right);
cgjmp_always(Lstart);
cgprint_label(Lend);
return (-1);
}
struct ASTbinnode *x = (struct ASTbinnode*)rt;
int lc = cgenerate_ast(x->left);
int rc = cgenerate_ast(x->right);
if (rt->op == A_ADD) {
return (cgarith_i32(lc, rc, "add nsw"));
} else if (rt->op == A_SUB) {
return (cgarith_i32(lc, rc, "sub nsw"));
} else if (rt->op == A_MUL) {
return (cgarith_i32(lc, rc, "mul nsw"));
} else if (rt->op == A_DIV) {
return (cgarith_i32(lc, rc, "sdiv"));
} else if (rt->op == A_EQ) {
return (cgcomp_i32(lc, rc, "eq"));
} else if (rt->op == A_NE) {
return (cgcomp_i32(lc, rc, "ne"));
} else if (rt->op == A_GT) {
return (cgcomp_i32(lc, rc, "sgt"));
} else if (rt->op == A_GE) {
return (cgcomp_i32(lc, rc, "sge"));
} else if (rt->op == A_LT) {
return (cgcomp_i32(lc, rc, "slt"));
} else if (rt->op == A_LE) {
return (cgcomp_i32(lc, rc, "sle"));
}
fail_ast_op(rt->op, __FUNCTION__);
} else if (nt == N_UN) {
struct ASTunnode *x = (struct ASTunnode*)rt;
int cv = cgenerate_ast(x->c);
if (rt->op == A_PRINT) {
cgprint(cv);
return (-1);
}
fail_ast_op(rt->op, __FUNCTION__);
} else if (nt == N_ASSIGN) {
struct ASTassignnode *x = (struct ASTassignnode*)rt;
int cv = cgenerate_ast(x->right);
if (rt->op == A_ASSIGN) {
return (cgstore_glob_i32(cv, array_get(&Gsym, x->left)));
}
fail_ast_op(rt->op, __FUNCTION__);
} else if (nt == N_MULTI) {
struct ASTblocknode *x = (struct ASTblocknode*)rt;
int val = -1;
struct llist_node *p = x->st.head;
while (p) {
val = cgenerate_ast(p->val);
p = p->nxt;
}
return val;
}
fail_ast_op(rt->op, __FUNCTION__);
}
// generate and write ir to Outfile
void cgllvm_generate(struct ASTnode* rt) {
for (int i = 0; i < Gsym.length; ++i) {
cginit_glob(array_get(&Gsym, i));
}
cgpreamble();
cgenerate_ast(rt);
cgpostamble();
}

View File

@ -2,6 +2,11 @@
#include <stdlib.h>
#include "scan.h"
void fail_target(const char *target_name) {
fprintf(stderr, "unknown target: %s.\n", target_name);
exit(1);
}
void fail_malloc(const char *func_name) {
fprintf(stderr, "%s: unable to malloc.\n", func_name);
exit(1);

View File

@ -5,11 +5,9 @@
#include "scan.h"
#include "token.h"
#include "ast.h"
#include "symbol.h"
#include "fatals.h"
static struct linklist Tokens; // current token for parsing
static int skip_semi = 0; // can skip statement semi (after block)
// Check that we have a binary operator and return its precedence.
// operators with larger precedence value will be evaluated first
@ -77,10 +75,12 @@ static void next(void) {
// preview next kth token from input stream
static struct token* preview(int k) {
if (Tokens.length <= k) {
static struct token token_eof;
token_eof = token_make_eof();
static struct token token_eof = {
.type = T_EOF
};
return (&token_eof);
}
struct token* res = llist_get(&Tokens, k);
return (res);
}
@ -92,9 +92,7 @@ static struct token* current(void) {
// match a token or report syntax error
static void match(int t) {
if (t == T_SEMI && skip_semi) {
skip_semi = 0;
} else if (current()->type == t) {
if (current()->type == t) {
next();
} else {
fail_ce_expect(token_typename[current()->type], token_typename[t]);
@ -102,7 +100,7 @@ static void match(int t) {
}
// check current token's type or report syntax error.
static void check(int t) {
static void expect(int t) {
if (current()->type != t) {
fail_ce_expect(token_typename[current()->type], token_typename[t]);
}
@ -122,11 +120,14 @@ static struct ASTnode* primary(void) {
res = expression();
match(T_RP);
} else if (current()->type == T_I32_LIT) {
res = ast_make_lit_i32(*(int32_t*)current()->val);
res = ast_make_lit_i32(current()->val_i32);
next();
} else if (current()->type == T_I64_LIT) {
res = ast_make_lit_i64(*(int64_t*)current()->val);
} else if (current()->type == T_INDENT) {
res = ast_make_lit_i64(current()->val_i64);
next();
} else if (current()->type == T_ID) {
// TODO: identifier.
/*
int id = findglob((char*)current()->val);
if (id == -1) {
fprintf(stderr, "syntax error on line %d: unknown indentifier %s.\n", Line, (char*)current()->val);
@ -134,6 +135,9 @@ static struct ASTnode* primary(void) {
}
next();
return (ast_make_var(id));
*/
res = NULL;
next();
} else {
fprintf(stderr, "syntax error on line %d: primary expression excpeted.\n", Line);
exit(1);
@ -178,7 +182,7 @@ static struct ASTnode* binexpr(int precedence) {
}
// parse one block of code, e.g. { a; b; }
static struct ASTnode* parse_block(void) {
static struct ASTnode* block(void) {
match(T_LB);
if (current()->type == T_RB) {
next();
@ -196,15 +200,15 @@ static struct ASTnode* parse_block(void) {
}
}
match(T_RB);
skip_semi = 1;
return ((struct ASTnode*)res);
}
// parse an expression
static struct ASTnode* expression(void) {
if (current()->type == T_LB) {
return (parse_block());
if (current()->type == T_SEMI) {
return (NULL);
}
return (binexpr(0));
}
@ -216,10 +220,11 @@ static struct ASTnode* print_statement(void) {
return (res);
}
/*
// parse variable declaration statement
static struct ASTnode* var_declaration(void) {
match(T_INT);
check(T_INDENT);
expect(T_IDENT);
if (findglob((char*)current()->val) != -1) {
fail_ce("variable declared twice.");
}
@ -228,6 +233,7 @@ static struct ASTnode* var_declaration(void) {
match(T_SEMI);
return (NULL);
}
*/
// parse an if statement
static struct ASTnode* if_statement(void) {
@ -284,6 +290,10 @@ static struct ASTnode* for_statement(void) {
if (body == NULL && inc == NULL) {
wbody = NULL;
} else if (body == NULL) {
wbody = inc;
} else if (inc == NULL) {
wbody = body;
} else {
struct ASTblocknode* wt = (struct ASTblocknode*)ast_make_block();
llist_pushback_notnull(&wt->st, body);
@ -293,40 +303,80 @@ static struct ASTnode* for_statement(void) {
llist_pushback_notnull(&container->st, init);
llist_pushback(&container->st, ast_make_binary(A_WHILE, cond, wbody));
return (struct ASTnode*)container;
return ((struct ASTnode*)container);
}
static struct ASTnode* return_statement() {
match(T_RETURN);
struct ASTnode *res = expression();
match(T_SEMI);
return (ast_make_unary(A_RETURN, res));
}
// parse one statement
static struct ASTnode* statement(void) {
switch (current()->type) {
case T_LB:
return (block());
case T_SEMI:
return (NULL);
case T_PRINT:
return (print_statement());
case T_INT:
return (var_declaration());
// case T_INT:
// return (var_declaration());
case T_IF:
return (if_statement());
case T_WHILE:
return (while_statement());
case T_FOR:
return (for_statement());
default:
skip_semi = 0;
case T_RETURN:
return (return_statement());
default: {
struct ASTnode* res = expression();
match(T_SEMI);
return (res);
}
}
}
// Parse one top-level function
// Sets the func_name param.
static struct ASTnode* function(char **func_name) {
match(T_INT);
expect(T_ID);
*func_name = current()->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.
next();
match(T_LP);
if (current()->type == T_VOID) {
next();
goto END_PARAM_LIST;
}
// TODO: param list
END_PARAM_LIST:
match(T_RP);
return (block());
}
// Parse ans return the full ast
struct ASTnode* parse(const char *name) {
Tokens = scan_tokens(name);
struct ASTnode* res = statement();
struct llist_node *p = Tokens.head;
while (p != Tokens.tail) {
free(p->val);
p = p->nxt;
char *func_name;
struct ASTnode* res = function(&func_name);
free(func_name);
while (Tokens.length > 0) {
token_free(llist_popfront(&Tokens));
}
llist_free(&Tokens);
return (res);

65
src/print_ast.c Normal file
View File

@ -0,0 +1,65 @@
#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "print_ast.h"
#include "symbol.h"
#include "fatals.h"
#include "util/array.h"
static int tabs;
static void print_tabs(FILE* Outfile) {
for (int i = 0; i < tabs; ++i) {
fprintf(Outfile, "\t");
}
}
static void ast_dfs(FILE* Outfile, struct ASTnode *x) {
print_tabs(Outfile);
if (x == NULL) {
fprintf(Outfile, "--->NULL.\n");
return;
}
switch(x->op) {
case A_RETURN: case A_PRINT: {
struct ASTunnode *t = x;
fprintf(Outfile, "--->UNOP(%s)\n", ast_opname[x->op]);
tabs += 1;
ast_dfs(Outfile, t->left);
tabs -= 1;
} break;
case A_LIT_I32: {
struct ASTi32node *t = x;
fprintf(Outfile, "--->INT32(%d)\n", t->val);
} break;
case A_LIT_I64: {
struct ASTi64node *t = x;
fprintf(Outfile, "--->INT64(%lld)\n", t->val);
} break;
case A_BLOCK: {
struct ASTblocknode *t = x;
fprintf(Outfile, "--->BLOCK(%d statements)\n", t->st.length);
tabs += 1;
struct llist_node *p = t->st.head;
while (p) {
ast_dfs(Outfile, p);
p = p->nxt;
}
tabs -= 1;
} break;
default: {
fprintf(Outfile, "--->%s...\n", ast_opname[x->op]);
} break;
}
}
void debug_ast_print(FILE *Outfile, struct ASTnode *rt) {
tabs = 0;
ast_dfs(Outfile, rt);
}

View File

@ -41,7 +41,7 @@ static void skip_whitespaces(void) {
// Scan and return an integer literal value from the input file.
static void scan_int(struct token *t) {
long long res = 0;
int64_t res = 0;
int c = preview();
while ('0' <= c && c <= '9') {
res = res * 10 + (c - '0');
@ -51,12 +51,10 @@ static void scan_int(struct token *t) {
if (INT32_MIN <= res && res <= INT32_MAX) {
t->type = T_I32_LIT;
t->val = malloc_or_fail(sizeof(int32_t), __FUNCTION__);
*((int32_t *)t->val) = (int)res;
t->val_i32 = (int32_t)res;
} else {
t->type = T_I64_LIT;
t->val = malloc_or_fail(sizeof(int64_t), __FUNCTION__);
*((int64_t *)t->val) = res;
t->val_i64 = (int64_t)res;
}
}
@ -102,6 +100,7 @@ static bool scan_keyword(struct token *t, char *s) {
"else",
"while",
"for",
"return",
NULL
};
@ -114,7 +113,8 @@ static bool scan_keyword(struct token *t, char *s) {
T_ELSE,
T_WHILE,
T_FOR,
-1
T_RETURN,
T_EXCEED,
};
for (int i = 0; map_s[i] != NULL; ++i) {
@ -139,7 +139,7 @@ static bool scan_1c(struct token *t) {
{'(', T_LP},
{')', T_RP},
{';', T_SEMI},
{'\0', -1}
{'\0', T_EXCEED}
};
int c = preview();
@ -156,7 +156,6 @@ static bool scan_1c(struct token *t) {
// Scan and return the next token found in the input.
static struct token* scan(void) {
struct token *t = malloc_or_fail(sizeof(struct token), __FUNCTION__);
t->val = NULL;
skip_whitespaces();
int c = preview();
@ -203,20 +202,16 @@ static struct token* scan(void) {
next();
}
} else {
// If it's a digit, scan the integer literal value in
if (isdigit(c)) {
if (isdigit(c)) { // If it's a digit, scan the integer literal value in
scan_int(t);
} else if (isalpha(c) || c == '_') {
t->val = scan_indentifier(NULL);
if (scan_keyword(t, t->val)) {
// got a keyword
free(t->val);
t->val = NULL;
} else {
// not a keyword, so it should be an indentifier.
t->type = T_INDENT;
t->val_s = scan_indentifier(NULL);
if (scan_keyword(t, t->val_s)) { // got a keyword
free(t->val_s);
} else { // not a keyword, so it should be an indentifier.
t->type = T_ID;
}
} else {
} else { // cannot match to anything we know, report error.
fail_char(c);
}
}
@ -243,3 +238,4 @@ struct linklist scan_tokens(const char *name) {
fclose(Infile);
return (res);
}

View File

@ -1,3 +1,4 @@
/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -119,4 +120,4 @@ int addglob(char *s) {
trie_set(ss, res);
return (res);
}
*/

22
src/target.c Normal file
View File

@ -0,0 +1,22 @@
#include <string.h>
#include "fatals.h"
#include "target.h"
int target_parse(const char *target_string) {
static const char *target_map_k[] = {
"ast",
NULL
};
static const int target_map_v[] = {
TARGET_AST,
};
for (int i = 0; target_map_k[i]; ++i) {
if (strcmp(target_map_k[i], target_string) == 0) {
return (target_map_v[i]);
}
}
fail_target(target_string);
}

View File

@ -1,5 +1,4 @@
#include <stdlib.h>
#include <stdio.h>
#include "token.h"
const char *token_typename[63] = {
@ -10,20 +9,18 @@ const char *token_typename[63] = {
"+", "-", "*", "/",
"==", "!=", "<", ">", "<=", ">=",
"int", "void", "char", "long",
"print", "if", "else", "while", "for",
"a signed integer literal (size 32)", "a signed integer literal (size 64)", "an indentifier"
"short",
"print", "if", "else",
"while", "for",
"return",
"a integer literal (16bit)", "a integer literal (32bit)", "a integer literal (64bit)",
"an identifier",
NULL
};
void token_free(struct token *t) {
if (t->val) {
free(t->val);
if (t->type == T_ID && t->val_s) {
free(t->val_s);
}
free(t);
}
struct token token_make_eof(void) {
struct token res;
res.type = T_EOF;
res.val = NULL;
return (res);
}

View File

@ -1,28 +1,21 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "fatals.h"
#include "util/linklist.h"
// Create a linklist node using given value.
struct llist_node* llist_createnode(void *val) {
struct llist_node *res = malloc(sizeof(struct llist_node));
if (res == NULL) {
fail_malloc(__FUNCTION__);
}
res->nxt = NULL;
res->val = val;
return (res);
}
// Appends an element in the linklist.
void llist_pushback(struct linklist *l, void *val) {
struct llist_node *x = (struct llist_node*)val;
x->nxt = NULL;
l->length += 1;
if (!l->tail) {
l->head = l->tail = llist_createnode(val);
l->head = l->tail = val;
return;
}
l->tail->nxt = llist_createnode(val);
l->tail = l->tail->nxt;
l->tail->nxt = x;
l->tail = x;
}
// A variant of pushback
@ -33,7 +26,7 @@ void llist_pushback_notnull(struct linklist *l, void *val) {
}
}
// Returns the _index_ thh element.
// Returns the _index_ th element.
void* llist_get(struct linklist *l, int index) {
if (index >= l->length) {
fprintf(stderr, "linklist out of range.\n");
@ -44,21 +37,12 @@ void* llist_get(struct linklist *l, int index) {
for (int i = 0; i < index; ++i) {
p = p->nxt;
}
return (p->val);
return (p);
}
// Modify the _index_ thh element.
void llist_set(struct linklist *l, int index, void *val) {
if (index >= l->length) {
fprintf(stderr, "linklist out of range.\n");
abort();
}
struct llist_node *p = l->head;
for (int i = 0; i < index; ++i) {
p = p->nxt;
}
p->val = val;
// Check if the given linklist is empty
bool llist_isempty(struct linklist *l) {
return (l->length == 0);
}
// Init a empty linklist.
@ -68,8 +52,8 @@ void llist_init(struct linklist *l) {
l->tail = NULL;
}
// Frees the linklist.
// Caller must make sure all elements in the linklist has already been freed.
// Frees all elements in the link list.
// Memory leaks if linklist element is still containing some pointer.
void llist_free(struct linklist *l) {
struct llist_node *p = l->head;
struct llist_node *nxt;
@ -81,22 +65,6 @@ void llist_free(struct linklist *l) {
llist_init(l);
}
// Frees the linklist.
// Callee will free all elements in the link list.
void llist_free_full(struct linklist *l) {
struct llist_node *p = l->head;
struct llist_node *nxt;
while (p) {
nxt = p->nxt;
if (p->val) {
free(p->val);
}
free(p);
p = nxt;
}
llist_init(l);
}
// Insert _val_ into the linklist as the _index_ th element.
// If _index_ is too large, pushback only!
void llist_insert(struct linklist *l, int index, void *val) {
@ -105,8 +73,8 @@ void llist_insert(struct linklist *l, int index, void *val) {
return;
}
struct llist_node *x = (struct llist_node*)val;
l->length += 1;
struct llist_node *x = llist_createnode(val);
if (index == 0) {
x->nxt = l->head;
l->head = x;
@ -129,16 +97,13 @@ void* llist_popfront(struct linklist *l) {
}
l->length -= 1;
void *res = l->head->val;
struct llist_node *res = l->head;
l->head = res->nxt;
res->nxt = NULL;
if (l->length == 0) {
free(l->head);
l->head = l->tail = NULL;
return (res);
}
struct llist_node *p = l->head;
l->head = p->nxt;
free(p);
return (res);
}
@ -160,7 +125,6 @@ void* llist_remove(struct linklist *l, int index) {
}
struct llist_node *q = p->nxt;
p->nxt = q->nxt;
void *res = q->val;
free(q);
return res;
q->nxt = NULL;
return (q);
}

View File

@ -1,4 +0,0 @@
{ print 12 * 3;
print 18 - 2 * 4;
print 1 + 2 + 9 - 5/2 + 3*5;
}

View File

@ -1,3 +0,0 @@
36
10
25

View File

@ -1,7 +0,0 @@
{
int fred;
int jim;
fred= 5;
jim= 12;
print fred + jim;
}

View File

@ -1 +0,0 @@
17

View File

@ -1,8 +0,0 @@
{
int x;
x= 1; print x;
x= x + 1; print x;
x= x + 1; print x;
x= x + 1; print x;
x= x + 1; print x;
}

View File

@ -1,5 +0,0 @@
1
2
3
4
5

View File

@ -1,12 +0,0 @@
{
int x;
x= 7 < 9; print x;
x= 7 <= 9; print x;
x= 7 != 9; print x;
x= 7 == 7; print x;
x= 7 >= 7; print x;
x= 7 <= 7; print x;
x= 9 > 7; print x;
x= 9 >= 7; print x;
x= 9 != 7; print x;
}

View File

@ -1,9 +0,0 @@
1
1
1
1
1
1
1
1
1

View File

@ -1,9 +0,0 @@
{
int i; int j;
i=6; j=12;
if (i < j) {
print i;
} else {
print j;
}
}

View File

@ -1 +0,0 @@
6

View File

@ -1,7 +0,0 @@
{ int i;
i=1;
while (i <= 10) {
print i;
i= i + 1;
}
}

View File

@ -1,10 +0,0 @@
1
2
3
4
5
6
7
8
9
10

View File

@ -1,6 +0,0 @@
{
int i;
for (i= 1; i <= 10; i= i + 1) {
print i;
}
}

View File

@ -1,10 +0,0 @@
1
2
3
4
5
6
7
8
9
10

View File

@ -1,6 +0,0 @@
{
long a = 5;
long b = a + 5;
int c = b - 1;
print c;
}

View File

@ -1 +0,0 @@
9

View File

@ -0,0 +1,3 @@
int main( {
return 0;
}

View File

@ -0,0 +1,3 @@
int main() {
return;
}

2
tests/invalid/no_brace.c Normal file
View File

@ -0,0 +1,2 @@
int main() {
return 0;

View File

@ -0,0 +1,3 @@
int main() {
return 0
}

3
tests/invalid/no_space.c Normal file
View File

@ -0,0 +1,3 @@
int main() {
return0;
}

View File

@ -0,0 +1,3 @@
int main() {
RETURN 0;
}

View File

@ -1,50 +0,0 @@
local acc_path = nil
for _, path in ipairs({ "../acc", "../acc.exe", "../acc.out" }) do
if os.exists(path) then
acc_path = path
end
end
if acc_path == nil then
error("acc executable not found.")
end
local function compare_file(p1, p2)
local f1 = io.open(p1, "r");
local f2 = io.open(p2, "r");
repeat
local l1 = f1:read()
local l2 = f2:read()
if l1 ~= l2 then
f1:close()
f2:close()
return false
end
until l1 == nil
f1:close()
f2:close()
return true
end
for _, input in ipairs(os.files("tests/input*")) do
if input:endswith(".ans") then
goto continue
end
local ans = input .. ".ans"
if not os.exists(ans) then
cprint("${orange} [WARN] not found answer for case %s.", input)
goto continue
end
os.run("acc llvm %s out.ll", input)
os.run("clang out.ll -o test.exe")
local output = os.iorun("test.exe")
local output_path = os.tmpfile()
io.writefile(output_path, output)
if compare_file(output_path, ans) then
cprint("${green} [INFO] case %s: OK.", input)
else
cprint("${red} [INFO] case %s: FAILED.", input)
end
::continue::
end

View File

@ -0,0 +1,3 @@
int main() {
return 100;
}

10
tests/valid/newlines.c Normal file
View File

@ -0,0 +1,10 @@
int
main
(
)
{
return
0
;
}

View File

@ -0,0 +1 @@
int main(){return 0;}

3
tests/valid/return_0.c Normal file
View File

@ -0,0 +1,3 @@
int main() {
return 0;
}

3
tests/valid/return_2.c Normal file
View File

@ -0,0 +1,3 @@
int main() {
return 2;
}

1
tests/valid/spaces.c Normal file
View File

@ -0,0 +1 @@
int main ( ) { return 0 ; }