unary operators
Signed-off-by: szdytom <szdytom@163.com>
This commit is contained in:
parent
38fd3c8911
commit
4cbe070d88
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
79
src/parse.c
79
src/parse.c
@ -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);
|
||||
|
61
src/quad.c
61
src/quad.c
@ -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: {
|
||||
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: {
|
||||
|
24
src/scan.c
24
src/scan.c
@ -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);
|
||||
|
@ -7,6 +7,8 @@ const char *token_typename[63] = {
|
||||
"{", "}", "(", ")",
|
||||
"=",
|
||||
"+", "-", "*", "/",
|
||||
"!", "&&", "||",
|
||||
"~",
|
||||
"==", "!=", "<", ">", "<=", ">=",
|
||||
"int", "void", "char", "long",
|
||||
"short",
|
||||
|
3
tests/invalid/missing_const.c
Normal file
3
tests/invalid/missing_const.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return !;
|
||||
}
|
3
tests/invalid/missing_semicolon.c
Normal file
3
tests/invalid/missing_semicolon.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return !5
|
||||
}
|
3
tests/invalid/nested_missing_const.c
Normal file
3
tests/invalid/nested_missing_const.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return !~;
|
||||
}
|
3
tests/invalid/wrong_order.c
Normal file
3
tests/invalid/wrong_order.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 4-;
|
||||
}
|
3
tests/valid/bitwise.c
Normal file
3
tests/valid/bitwise.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return ~12;
|
||||
}
|
3
tests/valid/bitwise_zero.c
Normal file
3
tests/valid/bitwise_zero.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return ~0;
|
||||
}
|
3
tests/valid/neg.c
Normal file
3
tests/valid/neg.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return -5;
|
||||
}
|
3
tests/valid/nested_ops.c
Normal file
3
tests/valid/nested_ops.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return !-3;
|
||||
}
|
3
tests/valid/nested_ops_2.c
Normal file
3
tests/valid/nested_ops_2.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return -------!!!----~0;
|
||||
}
|
3
tests/valid/not_five.c
Normal file
3
tests/valid/not_five.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return !5;
|
||||
}
|
3
tests/valid/not_zero.c
Normal file
3
tests/valid/not_zero.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return !0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user