refactory code

This commit is contained in:
方而静 2023-02-07 21:32:38 +08:00
parent 1d6c3ce4c0
commit 440bc22ac7
24 changed files with 231 additions and 211 deletions

3
.gitignore vendored
View File

@ -8,6 +8,9 @@
*.obj
*.elf
# Debugger
*.pdb
# Linker output
*.ilk
*.map

View File

@ -1,8 +1,10 @@
#ifndef ACC_FATALS_H
#define ACC_FATALS_H
#include "noreturn.h"
#include <stddef.h>
#include <stdnoreturn.h>
void* malloc_or_fail(size_t s, const char *func_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);

View File

@ -1,6 +0,0 @@
#ifndef ACC_NORETURN_H
#define ACC_NORETURN_H
#define noreturn _Noreturn
#endif

View File

@ -19,9 +19,9 @@ enum {
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_INTLIT, T_LONGLIT, T_INDENT,
T_I32_LIT, T_I64_LIT, T_INDENT,
};
extern const char *token_typename[29];
extern const char *token_typename[63];
void token_free(struct token *t);
struct token token_make_eof(void);

View File

@ -13,13 +13,19 @@ struct linklist {
};
struct llist_node* llist_createnode(void *val);
void llist_pushback(struct linklist *l, void *val);
void llist_pushback_notnull(struct linklist *l, void *val);
void* llist_get(struct linklist *l, int x);
void llist_set(struct linklist *l, int x, void *val);
void llist_init(struct linklist *l);
void llist_free(struct linklist *l);
void llist_free_full(struct linklist *l);
void llist_pushback(struct linklist *l, void *val);
void llist_pushback_notnull(struct linklist *l, void *val);
void* llist_get(struct linklist *l, int x);
void llist_set(struct linklist *l, int x, void *val);
void llist_insert(struct linklist *l, int x, void *val);
void llist_popfront(struct linklist *l);
void* llist_popfront(struct linklist *l);
void* llist_remove(struct linklist *l, int index);
#endif

View File

@ -1,7 +1,9 @@
#ifndef ACC_UTIL_MISC_H
#define ACC_UTIL_MISC_H
int strequal(const char *s1, const char *s2);
#include <stdbool.h>
bool strequal(const char *s1, const char *s2);
char *strclone(const char *s);
#endif

View File

@ -6,10 +6,7 @@
// Build and return a binary AST node
struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *right) {
struct ASTbinnode *x = malloc(sizeof(struct ASTbinnode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTbinnode *x = malloc_or_fail(sizeof(struct ASTbinnode), __FUNCTION__);
x->op = op;
x->left = left;
@ -19,10 +16,7 @@ struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *ri
// Make an AST int literal node
struct ASTnode* ast_make_intlit(int val) {
struct ASTintnode *x = malloc(sizeof(struct ASTintnode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTintnode *x = malloc_or_fail(sizeof(struct ASTintnode), __FUNCTION__);
x->op = A_INTLIT;
x->val = val;
@ -31,10 +25,7 @@ struct ASTnode* ast_make_intlit(int val) {
// Make an AST variable value node
struct ASTnode* ast_make_var(int id) {
struct ASTvarnode *x = malloc(sizeof(struct ASTvarnode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTvarnode *x = malloc_or_fail(sizeof(struct ASTvarnode), __FUNCTION__);
x->op = A_VAR;
x->id = id;
@ -43,10 +34,7 @@ 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 ASTunnode *x = malloc(sizeof(struct ASTunnode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTunnode *x = malloc_or_fail(sizeof(struct ASTunnode), __FUNCTION__);
x->op = op;
x->c = c;
@ -55,10 +43,7 @@ struct ASTnode* ast_make_unary(int op, struct ASTnode *c) {
// Make a block ast node
struct ASTnode* ast_make_block() {
struct ASTblocknode *x = malloc(sizeof(struct ASTblocknode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTblocknode *x = malloc_or_fail(sizeof(struct ASTblocknode), __FUNCTION__);
x->op = A_BLOCK;
llist_init(&x->st);
@ -67,10 +52,7 @@ struct ASTnode* ast_make_block() {
// Make a assignment ast node
struct ASTnode* ast_make_assign(int op, int left, struct ASTnode *right) {
struct ASTassignnode *x = malloc(sizeof(struct ASTassignnode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTassignnode *x = malloc_or_fail(sizeof(struct ASTassignnode), __FUNCTION__);
x->op = op;
x->left = left;
@ -80,10 +62,7 @@ struct ASTnode* ast_make_assign(int op, int left, struct ASTnode *right) {
// Make a if statement ast node
struct ASTnode* ast_make_if(struct ASTnode *left, struct ASTnode *right, struct ASTnode *cond) {
struct ASTifnode *x = malloc(sizeof(struct ASTifnode));
if (x == NULL) {
fail_malloc(__FUNCTION__);
}
struct ASTifnode *x = malloc_or_fail(sizeof(struct ASTifnode), __FUNCTION__);
x->op = A_IF;
x->left = left;

View File

@ -53,7 +53,7 @@ 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 (ty[0] != 'i' || ty[1] != '1' || ty[2] != '\0') {
if (strcmp(ty, "i1") != 0) { // special case for bool
fprintf(Outfile, "\t%%%d = zext i1 %%%d to %s\n", r2, r1, ty);
}
return (r2);

View File

@ -7,6 +7,14 @@ void fail_malloc(const char *func_name) {
exit(1);
}
void* malloc_or_fail(size_t s, const char *func_name) {
void *res = malloc(s);
if (res == NULL) {
fail_malloc(func_name);
}
return (res);
}
void fail_ast_op(int op, const char *func_name) {
fprintf(stderr, "%s: unknown ast operator %d.\n", func_name, op);
exit(1);

View File

@ -1,5 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include "scan.h"
#include "token.h"
#include "ast.h"
@ -42,10 +44,10 @@ static int arithop(int t) {
{T_GT, A_GT},
{T_GE, A_GE},
{T_ASSIGN, A_ASSIGN},
{T_EOF}
{-1}
};
for (int i = 0; map[i][0] != T_EOF; ++i) {
for (int i = 0; map[i][0] != -1; ++i) {
if (t == map[i][0]) {
return map[i][1];
}
@ -54,22 +56,21 @@ static int arithop(int t) {
}
// operator ssociativity direction
// Return 0 if left to right, e.g. +
// 1 if right to left, e.g. =
static int direction_rtl(int t) {
// Returns false if left to right, e.g. +
// true if right to left, e.g. =
static bool direction_rtl(int t) {
switch(t) {
case T_ASSIGN:
return (1);
return (true);
default:
return (0);
return (false);
}
}
// Next token
static void next(void) {
if (Tokens.head) {
token_free(Tokens.head->val);
llist_popfront(&Tokens);
token_free(llist_popfront(&Tokens));
}
}
@ -78,7 +79,8 @@ static struct token preview(int k) {
if (Tokens.length <= k) {
return (token_make_eof());
}
return (*((struct token*)llist_get(&Tokens, k)));
struct token* res = llist_get(&Tokens, k);
return (*res);
}
// return current token from input stream
@ -117,12 +119,12 @@ static struct ASTnode* primary(void) {
next();
res = expression();
match(T_RP);
} else if (current().type == T_INTLIT) {
res = ast_make_intlit(*((int*)current().val));
} else if (current().type == T_I32_LIT) {
res = ast_make_intlit(*(int32_t*)current().val);
next();
} else if (current().type == T_LONGLIT) {
} else if (current().type == T_I64_LIT) {
// todo
fprintf(stderr, "TOOD.\n");
fprintf(stderr, "TOOD: T_I64_LIT.\n");
exit(1);
} else if (current().type == T_INDENT) {
int id = findglob((char*)current().val);
@ -296,29 +298,24 @@ static struct ASTnode* for_statement(void) {
// parse one statement
static struct ASTnode* statement(void) {
if (current().type == T_SEMI) {
return (NULL);
}
else if (current().type == T_PRINT) {
return (print_statement());
}
else if (current().type == T_INT) {
return (var_declaration());
}
else if (current().type == T_IF) {
return (if_statement());
}
else if (current().type == T_WHILE) {
return (while_statement());
}
else if (current().type == T_FOR) {
return (for_statement());
}
else {
skip_semi = 0;
struct ASTnode* res = expression();
match(T_SEMI);
return (res);
switch (current().type) {
case T_SEMI:
return (NULL);
case T_PRINT:
return (print_statement());
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;
struct ASTnode* res = expression();
match(T_SEMI);
return (res);
}
}

View File

@ -1,8 +1,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "token.h"
#include "fatals.h"
#include "util/misc.h"
@ -11,18 +12,6 @@ int Line = 1;
static int Preview;
static FILE *Infile;
const char *token_typename[] = {
"EOF",
";",
"{", "}", "(", ")",
"=",
"+", "-", "*", "/",
"==", "!=", "<", ">", "<=", ">=",
"int", "void", "char", "long",
"print", "if", "else", "while", "for",
"an integer literal (type int)", "an integer literal (type long)", "an indentifier"
};
// preview one char, not getting it out from the stream
static int preview(void) {
if (!Preview) {
@ -43,9 +32,7 @@ static void next(void) {
// Skip past input that we don't need to deal with,
// i.e. whitespace, newlines.
static void skip_whitespaces(void) {
int c;
c = preview();
int c = preview();
while (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f') {
next();
c = preview();
@ -53,7 +40,7 @@ static void skip_whitespaces(void) {
}
// Scan and return an integer literal value from the input file.
static void scanint(struct token *t) {
static void scan_int(struct token *t) {
long long res = 0;
int c = preview();
while ('0' <= c && c <= '9') {
@ -62,40 +49,35 @@ static void scanint(struct token *t) {
c = preview();
}
if (INT_MIN <= res && res <= INT_MAX) {
t->type = T_INTLIT;
t->val = malloc(sizeof(int));
if (t->val == NULL) {
fail_malloc(__FUNCTION__);
}
*((int *)t->val) = (int)res;
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;
} else {
t->type = T_LONGLIT;
t->val = malloc(sizeof(long long));
if (t->val == NULL) {
fail_malloc(__FUNCTION__);
}
*((long long *)t->val) = res;
t->type = T_I64_LIT;
t->val = malloc_or_fail(sizeof(int64_t), __FUNCTION__);
*((int64_t *)t->val) = res;
}
}
// Scan an identifier from the input file and
// Return the identifier (char*)
// Return the identifier string (char*)
// Writes the length into _n_ or NULL
static char* scan_indentifier(int *n) {
int sz = 128, len = 0;
char *res = malloc(sz * sizeof(char));
char *res = malloc_or_fail(sz * sizeof(char), __FUNCTION__);
memset(res, 0, sz * sizeof(char));
int c = preview();
while (isdigit(c) || isalpha(c) || c == '_') {
if (len >= sz - 1) {
sz *= 2;
char *old = res;
res = malloc(sz * sizeof(char));
memcpy(res, old, len * sizeof(char));
char *res = realloc(res, sz * sizeof(char));
if (res == NULL) {
fail_malloc(__FUNCTION__);
}
memset(res + len * sizeof(char), 0, (sz - len) * sizeof(char));
free(old);
}
res[len++] = c;
next();
@ -108,8 +90,9 @@ static char* scan_indentifier(int *n) {
return (res);
}
// Given a word from the input, scan if it is a keyword
static int scan_keyword(struct token *t, char *s) {
// Given a word from the input, scan if it is a keyword.
// Returns true if found keyword.
static bool scan_keyword(struct token *t, char *s) {
static const char *map_s[] = {
"print",
"int",
@ -137,15 +120,15 @@ static int scan_keyword(struct token *t, char *s) {
for (int i = 0; map_s[i] != NULL; ++i) {
if (strequal(map_s[i], s)) {
t->type = map_t[i];
return (1);
return (true);
}
}
return (0);
return (false);
}
// Scan one char token
// Return 1 if found
static int scan_1c(struct token *t) {
static bool scan_1c(struct token *t) {
static const int map[][2] = {
{'+', T_PLUS},
{'-', T_MINUS},
@ -164,18 +147,15 @@ static int scan_1c(struct token *t) {
if (map[i][0] == c) {
t->type = map[i][1];
next();
return (1);
return (true);
}
}
return (0);
return (false);
}
// Scan and return the next token found in the input.
static struct token* scan(void) {
struct token *t = malloc(sizeof(struct token));
if (t == NULL) {
fail_malloc(__FUNCTION__);
}
struct token *t = malloc_or_fail(sizeof(struct token), __FUNCTION__);
t->val = NULL;
skip_whitespaces();
@ -204,8 +184,7 @@ static struct token* scan(void) {
t->type = T_NE;
next();
} else {
fprintf(stderr, "Unrecognised character %c on line %d.\n", c, Line);
exit(1);
fail_char(c);
}
} else if (c == '<') {
t->type = T_LT;
@ -226,7 +205,7 @@ static struct token* scan(void) {
} else {
// If it's a digit, scan the integer literal value in
if (isdigit(c)) {
scanint(t);
scan_int(t);
} else if (isalpha(c) || c == '_') {
t->val = scan_indentifier(NULL);
if (scan_keyword(t, t->val)) {

View File

@ -2,6 +2,18 @@
#include <stdio.h>
#include "token.h"
const char *token_typename[63] = {
"EOF",
";",
"{", "}", "(", ")",
"=",
"+", "-", "*", "/",
"==", "!=", "<", ">", "<=", ">=",
"int", "void", "char", "long",
"print", "if", "else", "while", "for",
"a signed integer literal (size 32)", "a signed integer literal (size 64)", "an indentifier"
};
void token_free(struct token *t) {
if (t->val) {
free(t->val);

View File

@ -3,6 +3,7 @@
#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) {
@ -13,6 +14,7 @@ struct llist_node* llist_createnode(void *val) {
return (res);
}
// Appends an element in the linklist.
void llist_pushback(struct linklist *l, void *val) {
l->length += 1;
if (!l->tail) {
@ -23,44 +25,51 @@ void llist_pushback(struct linklist *l, void *val) {
l->tail = l->tail->nxt;
}
// A variant of pushback
// Only does pushback if _val_ is not null.
void llist_pushback_notnull(struct linklist *l, void *val) {
if (val) {
llist_pushback(l, val);
}
}
void* llist_get(struct linklist *l, int x) {
if (x >= l->length) {
// Returns the _index_ thh element.
void* llist_get(struct linklist *l, int index) {
if (index >= l->length) {
fprintf(stderr, "linklist out of range.\n");
abort();
}
struct llist_node *p = l->head;
for (int i = 0; i < x; ++i) {
for (int i = 0; i < index; ++i) {
p = p->nxt;
}
return (p->val);
}
void llist_set(struct linklist *l, int x, void *val) {
if (x >= l->length) {
// 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 < x; ++i) {
for (int i = 0; i < index; ++i) {
p = p->nxt;
}
p->val = val;
}
// Init a empty linklist.
void llist_init(struct linklist *l) {
l->length = 0;
l->head = NULL;
l->tail = NULL;
}
// Frees the linklist.
// Caller must make sure all elements in the linklist has already been freed.
void llist_free(struct linklist *l) {
struct llist_node *p = l->head;
struct llist_node *nxt;
@ -72,6 +81,24 @@ 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) {
if (index >= l->length) {
llist_pushback(l, val);
@ -94,18 +121,46 @@ void llist_insert(struct linklist *l, int index, void *val) {
p->nxt = x;
}
void llist_popfront(struct linklist *l) {
// Pop the first element of the link list
// Return the first element.
void* llist_popfront(struct linklist *l) {
if (l->head == NULL) {
return;
return (NULL);
}
l->length -= 1;
void *res = l->head->val;
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);
}
// Removes the _index_ th element from the linklist.
// Returns the removed value.
void* llist_remove(struct linklist *l, int index) {
if (index >= l->length) {
return (NULL);
}
if (index == 0) {
return (llist_popfront(l));
}
l->length -= 1;
struct llist_node *p = l->head;
for (int i = 0; i < index - 2; ++i) {
p = p->nxt;
}
struct llist_node *q = p->nxt;
p->nxt = q->nxt;
void *res = q->val;
free(q);
return res;
}

View File

@ -1,22 +1,15 @@
#include <string.h>
#include <stdlib.h>
#include "util/misc.h"
// check if two string are the same
int strequal(const char *s1, const char *s2) {
for (int i = 1; ; ++i) {
if (s1[i] != s2[i]) {
return (0);
}
if (s1[i] == '\0') {
break;
}
}
return (1);
bool strequal(const char *s1, const char *s2) {
return (strcmp(s1, s2) == 0);
}
// A impl of C23 strdup()
char* strclone(char *s) {
// Clones the given string
char* strclone(const char *s) {
int n = strlen(s);
char *res = malloc(n + 1);
memcpy(res, s, n * sizeof(char));

50
tests/test_llvm.lua Normal file
View File

@ -0,0 +1,50 @@
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

@ -1,30 +0,0 @@
#!/bin/sh
# Run each test and compare
# against known good output
set -e
echo "Testing for target llvm..."
if [ ! -f ../acc ]
then echo "Need to build ../acc first!"; exit 1
fi
for i in input*
do if [ ! -f "out.$i" ]
then echo "Can't run test on $i, no answer file!"
else
echo -n $i
../acc llvm $i
clang -o out out.ll -w
./out > trial.$i
cmp -s "out.$i" "trial.$i"
if [ "$?" -eq "1" ]
then echo ": failed"
diff -c "out.$i" "trial.$i"
echo
else echo ": OK"
fi
rm -f out out.ll "trial.$i"
fi
done

View File

@ -1,30 +0,0 @@
#!/bin/sh
# Run each test and compare
# against known good output
set -e
echo "Testing for target x86_64..."
if [ ! -f ../acc ]
then echo "Need to build ../acc first!"; exit 1
fi
for i in input*
do if [ ! -f "out.$i" ]
then echo "Can't run test on $i, no answer file!"
else
echo -n $i
../acc x86_64 $i
gcc -o out out.s
./out > trial.$i
cmp -s "out.$i" "trial.$i"
if [ "$?" -eq "1" ]
then echo ": failed"
diff -c "out.$i" "trial.$i"
echo
else echo ": OK"
fi
rm -f out out.s "trial.$i"
fi
done