main function(AST)
Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
parent
6950807161
commit
121c37c16a
32
README.md
32
README.md
@ -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
|
||||
```
|
||||
|
@ -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
|
||||
|
29
include/cg.h
29
include/cg.h
@ -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
|
@ -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
7
include/print_ast.h
Normal 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
|
||||
|
@ -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);
|
||||
|
@ -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
11
include/target.h
Normal 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
|
@ -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
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
#ifndef ACC_UTIL_HASHMAP
|
||||
#define ACC_UTIL_HASHMAP
|
||||
|
||||
@ -10,3 +11,4 @@ struct hashmap {
|
||||
void hashmap_init();
|
||||
|
||||
#endif
|
||||
*/
|
||||
|
@ -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
43
main.c
@ -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
100
src/ast.c
@ -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);
|
||||
|
38
src/cg.c
38
src/cg.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
119
src/cg_ast.c
119
src/cg_ast.c
@ -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);
|
||||
}
|
||||
|
216
src/cg_llvm.c
216
src/cg_llvm.c
@ -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();
|
||||
}
|
@ -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);
|
||||
|
102
src/parse.c
102
src/parse.c
@ -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
65
src/print_ast.c
Normal 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);
|
||||
}
|
||||
|
34
src/scan.c
34
src/scan.c
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
22
src/target.c
Normal 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);
|
||||
}
|
21
src/token.c
21
src/token.c
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
{ print 12 * 3;
|
||||
print 18 - 2 * 4;
|
||||
print 1 + 2 + 9 - 5/2 + 3*5;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
36
|
||||
10
|
||||
25
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
int fred;
|
||||
int jim;
|
||||
fred= 5;
|
||||
jim= 12;
|
||||
print fred + jim;
|
||||
}
|
@ -1 +0,0 @@
|
||||
17
|
@ -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;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
@ -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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
int i; int j;
|
||||
i=6; j=12;
|
||||
if (i < j) {
|
||||
print i;
|
||||
} else {
|
||||
print j;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
6
|
@ -1,7 +0,0 @@
|
||||
{ int i;
|
||||
i=1;
|
||||
while (i <= 10) {
|
||||
print i;
|
||||
i= i + 1;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
int i;
|
||||
for (i= 1; i <= 10; i= i + 1) {
|
||||
print i;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
long a = 5;
|
||||
long b = a + 5;
|
||||
int c = b - 1;
|
||||
print c;
|
||||
}
|
@ -1 +0,0 @@
|
||||
9
|
3
tests/invalid/missing_paren.c
Normal file
3
tests/invalid/missing_paren.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main( {
|
||||
return 0;
|
||||
}
|
3
tests/invalid/missing_retval.c
Normal file
3
tests/invalid/missing_retval.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return;
|
||||
}
|
2
tests/invalid/no_brace.c
Normal file
2
tests/invalid/no_brace.c
Normal file
@ -0,0 +1,2 @@
|
||||
int main() {
|
||||
return 0;
|
3
tests/invalid/no_semicolon.c
Normal file
3
tests/invalid/no_semicolon.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 0
|
||||
}
|
3
tests/invalid/no_space.c
Normal file
3
tests/invalid/no_space.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return0;
|
||||
}
|
3
tests/invalid/wrong_case.c
Normal file
3
tests/invalid/wrong_case.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
RETURN 0;
|
||||
}
|
@ -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
|
3
tests/valid/multi_digit.c
Normal file
3
tests/valid/multi_digit.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 100;
|
||||
}
|
10
tests/valid/newlines.c
Normal file
10
tests/valid/newlines.c
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
int
|
||||
main
|
||||
(
|
||||
)
|
||||
{
|
||||
return
|
||||
0
|
||||
;
|
||||
}
|
1
tests/valid/no_newlines.c
Normal file
1
tests/valid/no_newlines.c
Normal file
@ -0,0 +1 @@
|
||||
int main(){return 0;}
|
3
tests/valid/return_0.c
Normal file
3
tests/valid/return_0.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
3
tests/valid/return_2.c
Normal file
3
tests/valid/return_2.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 2;
|
||||
}
|
1
tests/valid/spaces.c
Normal file
1
tests/valid/spaces.c
Normal file
@ -0,0 +1 @@
|
||||
int main ( ) { return 0 ; }
|
Loading…
x
Reference in New Issue
Block a user