unary operators

Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
方而静 2023-06-13 22:13:18 +08:00
parent 38fd3c8911
commit 4cbe070d88
19 changed files with 195 additions and 26 deletions

View File

@ -7,8 +7,10 @@
// AST operation types
enum {
A_ASSIGN,
A_ADD, A_SUB, A_MUL, A_DIV,
A_NEG, A_ADD, A_SUB, A_MUL, A_DIV,
A_EQ, A_NE, A_LT, A_GT, A_LE, A_GE,
A_LNOT, A_LAND, A_LOR,
A_BNOT,
A_LIT_I32, A_LIT_I64,
A_VAR,
A_BLOCK,

View File

@ -11,6 +11,11 @@ enum {
// Loading immediates
Q_IMM_I32, // integer (32bits)
// Arithmetic operations
Q_NEG, // negation
Q_NOT, // bitwise not
Q_CMP_EQ, // compare whether equal
// Terminates
Q_RET, // return
Q_BR_ALWAYS, // conditional goto: always goto true branch.

View File

@ -23,7 +23,9 @@ enum {
T_SEMI, // ;
T_LB, T_RB, T_LP, T_RP, // { } ( )
T_ASSIGN, // =
T_PLUS, T_MINUS, T_STAR, T_SLASH, // + - * /
T_PLUS, T_MINUS, T_STAR, T_SLASH, // - + - * /
T_LNOT, T_LAND, T_LOR, // ! && ||
T_BNOT, // ~
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

View File

@ -6,8 +6,10 @@
const char *ast_opname[] = {
"=",
"+", "-", "*", "/",
"neg", "add", "sub", "mul", "div",
"==", "!=", "<", ">", "<=", ">=",
"not", "and", "or",
"~",
"int32", "int64",
"var",
"block",
@ -188,7 +190,8 @@ void ast_free(struct ASTnode *x) {
ast_free(t->right);
} break;
case A_PRINT: case A_RETURN: {
case A_PRINT: case A_RETURN:
case A_LNOT: case A_BNOT: case A_NEG: {
struct ASTunnode *t = (struct ASTunnode*)x;
ast_free(t->left);
} break;

View File

@ -10,7 +10,7 @@
static struct linklist Tokens; // current token for parsing
// Check that we have a binary operator and return its precedence.
// operators with larger precedence value will be evaluated first
// Operators with larger precedence value will be evaluated first.
static int op_precedence(struct token *t) {
switch (t->type) {
case T_ASSIGN:
@ -28,8 +28,8 @@ static int op_precedence(struct token *t) {
}
}
// Convert a arithmetic token into an AST operation.
static int arithop(struct token *t) {
// Converts a binary arithmetic token into an AST operation.
static int binary_arithop(struct token *t) {
static const int map[][2] = {
{T_PLUS, A_ADD},
{T_MINUS, A_SUB},
@ -42,6 +42,8 @@ static int arithop(struct token *t) {
{T_GT, A_GT},
{T_GE, A_GE},
{T_ASSIGN, A_ASSIGN},
{T_LAND, A_LAND},
{T_LOR, A_LOR},
{-1}
};
@ -53,7 +55,25 @@ static int arithop(struct token *t) {
fail_ce_expect(t->line, "an binary operator", token_typename[t->type]);
}
// operator ssociativity direction
// Converts a unary arithmetic token into an AST operation.
static int unary_arithop(struct token *t) {
static const int map[][2] = {
{T_MINUS, A_NEG},
{T_LNOT, A_LNOT},
{T_BNOT, A_BNOT},
{-1}
};
for (int i = 0; map[i][0] != -1; ++i) {
if (t->type == map[i][0]) {
return map[i][1];
}
}
fail_ce_expect(t->line, "an unary operator", token_typename[t->type]);
}
// Operator associativity direction
// Returns false if left to right, e.g. +
// true if right to left, e.g. =
static bool direction_rtl(int t) {
@ -114,6 +134,7 @@ static struct ASTnode* expression(void);
static struct ASTnode* primary(void) {
struct ASTnode *res;
struct token *t = current();
if (t->type == T_LP) {
// ( expr ) considered as primary
next();
@ -143,16 +164,50 @@ static struct ASTnode* primary(void) {
return (res);
}
// Check if it is binary operator
static int is_binop(int t) {
return (T_ASSIGN <= t && t <= T_GE);
// Returns whether the given token type can be a prefix operator (negation, logical not, bitwise not)
static bool is_prefix_op(int op) {
switch (op) {
case T_MINUS: case T_LNOT: case T_BNOT:
return (true);
default:
return (false);
}
}
// Parses a primary expression with prefixes, e.g. ~10
static struct ASTnode* prefixed_primary(void) {
struct token *t = current();
if (is_prefix_op(t->type)) {
next();
struct ASTnode *child = prefixed_primary();
return (ast_make_unary(unary_arithop(t), child));
}
return (primary());
}
// Returns whether the given token type can be a binary operator.
static bool is_binop(int t) {
switch (t) {
case T_ASSIGN:
case T_PLUS: case T_MINUS: case T_STAR: case T_SLASH:
case T_LAND: case T_LOR:
case T_EQ: case T_NE: case T_LT:
case T_GT: case T_LE: case T_GE:
return (true);
default:
return (false);
}
}
// Return an AST tree whose root is a binary operator
static struct ASTnode* binexpr(int precedence) {
struct ASTnode *left, *right;
left = primary();
left = prefixed_primary();
struct token *op = current();
if (!is_binop(op->type)) {
return (left);
@ -164,10 +219,10 @@ static struct ASTnode* binexpr(int precedence) {
if (direction_rtl(op->type)) {
right = binexpr(precedence);
left = ast_make_assign(arithop(op), left, right);
left = ast_make_assign(binary_arithop(op), left, right);
} else {
right = binexpr(tp);
left = ast_make_binary(arithop(op), left, right); // join right into left
left = ast_make_binary(binary_arithop(op), left, right); // join right into left
}
op = current();
@ -304,7 +359,7 @@ static struct ASTnode* for_statement(void) {
return ((struct ASTnode*)container);
}
static struct ASTnode* return_statement() {
static struct ASTnode* return_statement(void) {
match(T_RETURN);
struct ASTnode *res = expression();
match(T_SEMI);
@ -347,7 +402,7 @@ static struct ASTnode* statement(void) {
// Parse one top-level function
// Sets the func_name param.
static struct Afunction* function() {
static struct Afunction* function(void) {
struct Afunction *res = afunc_make();
match(T_INT);

View File

@ -84,7 +84,20 @@ struct Qvar* qfunc_new_var(struct Qfunction *f) {
return (res);
}
// Translate an AST unary arithmetic opcode to a Quad opcode.
static int unary_arithop(int op) {
switch (op) {
case A_NEG: return (Q_NEG);
case A_BNOT: return (Q_NOT);
default: fail_quad_op(op, __FUNCTION__);
}
}
static struct Qvar* qcg_dfs(struct ASTnode *x, struct Qfunction *f, struct Qblock *b) {
if (x == NULL) {
return (NULL);
}
switch (x->op) {
case A_RETURN: {
struct ASTunnode *t = (void*)x;
@ -94,13 +107,6 @@ static struct Qvar* qcg_dfs(struct ASTnode *x, struct Qfunction *f, struct Qbloc
return (NULL);
}
case A_LIT_I32: {
struct ASTi32node *t = (void*)x;
struct Qvar *res = qfunc_new_var(f);
qblock_add_ins(b, quad_make_i32(res, t->val));
return (res);
}
case A_BLOCK: {
struct ASTblocknode *t = (void*)x;
struct llist_node *p = t->st.head;
@ -111,6 +117,31 @@ static struct Qvar* qcg_dfs(struct ASTnode *x, struct Qfunction *f, struct Qbloc
return (NULL);
}
case A_LIT_I32: {
struct Qvar *res = qfunc_new_var(f);
struct ASTi32node *t = (void*)x;
qblock_add_ins(b, quad_make_i32(res, t->val));
return (res);
}
case A_NEG: case A_BNOT: {
struct Qvar *res = qfunc_new_var(f);
struct ASTunnode *t = (void*)x;
struct Qvar *value = qcg_dfs(t->left, f, b);
qblock_add_ins(b, quad_make(unary_arithop(x->op), res, value, NULL));
return (res);
}
case A_LNOT: {
struct Qvar *res = qfunc_new_var(f);
struct ASTunnode *t = (void*)x;
struct Qvar *value = qcg_dfs(t->left, f, b);
struct Qvar *zero = qfunc_new_var(f);
qblock_add_ins(b, quad_make_i32(zero, 0));
qblock_add_ins(b, quad_make(Q_CMP_EQ, res, value, zero));
return (res);
}
default: {
fail_ast_op(x->op, __FUNCTION__);
}
@ -163,7 +194,23 @@ static void quad_debug_print(struct Quad *self, FILE *Outfile) {
} break;
case Q_RET: {
fprintf(Outfile, "\tret $%d.\n", self->left->id);
if (self->left) {
fprintf(Outfile, "\tret $%d.\n", self->left->id);
} else {
fputs("\tret.", Outfile);
}
} break;
case Q_NEG: {
fprintf(Outfile, "\t$%d = neg $%d;\n", self->dest->id, self->left->id);
} break;
case Q_NOT: {
fprintf(Outfile, "\t$%d = not $%d;\n", self->dest->id, self->left->id);
} break;
case Q_CMP_EQ: {
fprintf(Outfile, "\t$%d = eq $%d, $%d;\n", self->dest->id, self->left->id, self->right->id);
} break;
default: {

View File

@ -185,8 +185,7 @@ static struct token* scan(void) {
t->type = T_NE;
next();
} else {
// TODO: the not operator
fail_char(t->line, c);
t->type = T_LNOT;
}
} else if (c == '<') {
t->type = T_LT;
@ -204,6 +203,27 @@ static struct token* scan(void) {
t->type = T_GE;
next();
}
} else if (c == '~') {
t->type = T_BNOT;
next();
} else if (c == '&') {
next();
c = preview();
if (c == '&') {
t->type = T_LAND;
} else {
// TODO: bitwise and
fail_char(t->line, c);
}
} else if (c == '|') {
next();
c = preview();
if (c == '|') {
t->type = T_LOR;
} else {
// TODO: bitwise or
fail_char(t->line, c);
}
} else {
if (isdigit(c)) { // If it's a digit, scan the integer literal value in
scan_int(t);

View File

@ -7,6 +7,8 @@ const char *token_typename[63] = {
"{", "}", "(", ")",
"=",
"+", "-", "*", "/",
"!", "&&", "||",
"~",
"==", "!=", "<", ">", "<=", ">=",
"int", "void", "char", "long",
"short",

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

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

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

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

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

View File

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

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

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

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

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