Add keyword "int" and make variable definition mandatory

This commit is contained in:
Rui Ueyama 2019-08-05 22:44:44 +09:00
parent a3ae40fc7f
commit 91465fe4ac
6 changed files with 139 additions and 78 deletions

View File

@ -33,6 +33,7 @@ struct Token {
void error(char *fmt, ...);
void error_at(char *loc, char *fmt, ...);
void error_tok(Token *tok, char *fmt, ...);
Token *peek(char *s);
Token *consume(char *op);
Token *consume_ident(void);
void expect(char *op);
@ -52,6 +53,7 @@ extern Token *token;
typedef struct Var Var;
struct Var {
char *name; // Variable name
Type *ty; // Type
int offset; // Offset from RBP
};
@ -86,6 +88,7 @@ typedef enum {
ND_EXPR_STMT, // Expression statement
ND_VAR, // Variable
ND_NUM, // Integer
ND_NULL, // Empty statement
} NodeKind;
// AST node type
@ -141,7 +144,10 @@ struct Type {
Type *base;
};
extern Type *int_type;
bool is_integer(Type *ty);
Type *pointer_to(Type *base);
void add_type(Node *node);
//

View File

@ -38,6 +38,8 @@ static void store(void) {
// Generate code for a given node.
static void gen(Node *node) {
switch (node->kind) {
case ND_NULL:
return;
case ND_NUM:
printf(" push %ld\n", node->val);
return;

55
parse.c
View File

@ -46,9 +46,10 @@ static Node *new_var_node(Var *var, Token *tok) {
return node;
}
static Var *new_lvar(char *name) {
static Var *new_lvar(char *name, Type *ty) {
Var *var = calloc(1, sizeof(Var));
var->name = name;
var->ty = ty;
VarList *vl = calloc(1, sizeof(VarList));
vl->var = var;
@ -58,6 +59,7 @@ static Var *new_lvar(char *name) {
}
static Function *function(void);
static Node *declaration(void);
static Node *stmt(void);
static Node *stmt2(void);
static Node *expr(void);
@ -81,30 +83,46 @@ Function *program(void) {
return head.next;
}
// basetype = "int" "*"*
static Type *basetype(void) {
expect("int");
Type *ty = int_type;
while (consume("*"))
ty = pointer_to(ty);
return ty;
}
static VarList *read_func_param(void) {
VarList *vl = calloc(1, sizeof(VarList));
Type *ty = basetype();
vl->var = new_lvar(expect_ident(), ty);
return vl;
}
static VarList *read_func_params(void) {
if (consume(")"))
return NULL;
VarList *head = calloc(1, sizeof(VarList));
head->var = new_lvar(expect_ident());
VarList *head = read_func_param();
VarList *cur = head;
while (!consume(")")) {
expect(",");
cur->next = calloc(1, sizeof(VarList));
cur->next->var = new_lvar(expect_ident());
cur->next = read_func_param();
cur = cur->next;
}
return head;
}
// function = ident "(" params? ")" "{" stmt* "}"
// params = ident ("," ident)*
// function = basetype ident "(" params? ")" "{" stmt* "}"
// params = param ("," param)*
// param = basetype ident
static Function *function(void) {
locals = NULL;
Function *fn = calloc(1, sizeof(Function));
basetype();
fn->name = expect_ident();
expect("(");
fn->params = read_func_params();
@ -122,6 +140,23 @@ static Function *function(void) {
return fn;
}
// declaration = basetype ident ("=" expr) ";"
static Node *declaration(void) {
Token *tok = token;
Type *ty = basetype();
Var *var = new_lvar(expect_ident(), ty);
if (consume(";"))
return new_node(ND_NULL, tok);
expect("=");
Node *lhs = new_var_node(var, tok);
Node *rhs = expr();
expect(";");
Node *node = new_binary(ND_ASSIGN, lhs, rhs, tok);
return new_unary(ND_EXPR_STMT, node, tok);
}
static Node *read_expr_stmt(void) {
Token *tok = token;
return new_unary(ND_EXPR_STMT, expr(), tok);
@ -138,6 +173,7 @@ static Node *stmt(void) {
// | "while" "(" expr ")" stmt
// | "for" "(" expr? ";" expr? ";" expr? ")" stmt
// | "{" stmt* "}"
// | declaration
// | expr ";"
static Node *stmt2(void) {
Token *tok;
@ -200,6 +236,9 @@ static Node *stmt2(void) {
return node;
}
if (tok = peek("int"))
return declaration();
Node *node = read_expr_stmt();
expect(";");
return node;
@ -360,7 +399,7 @@ static Node *primary(void) {
// Variable
Var *var = find_var(tok);
if (!var)
var = new_lvar(strndup(tok->str, tok->len));
error_tok(tok, "undefined variable");
return new_var_node(var, tok);
}

124
test.sh
View File

@ -27,77 +27,83 @@ assert() {
fi
}
assert 0 'main() { return 0; }'
assert 42 'main() { return 42; }'
assert 21 'main() { return 5+20-4; }'
assert 41 'main() { return 12 + 34 - 5 ; }'
assert 47 'main() { return 5+6*7; }'
assert 15 'main() { return 5*(9-6); }'
assert 4 'main() { return (3+5)/2; }'
assert 10 'main() { return -10+20; }'
assert 10 'main() { return - -10; }'
assert 10 'main() { return - - +10; }'
assert 0 'int main() { return 0; }'
assert 42 'int main() { return 42; }'
assert 21 'int main() { return 5+20-4; }'
assert 41 'int main() { return 12 + 34 - 5 ; }'
assert 47 'int main() { return 5+6*7; }'
assert 15 'int main() { return 5*(9-6); }'
assert 4 'int main() { return (3+5)/2; }'
assert 10 'int main() { return -10+20; }'
assert 10 'int main() { return - -10; }'
assert 10 'int main() { return - - +10; }'
assert 0 'main() { return 0==1; }'
assert 1 'main() { return 42==42; }'
assert 1 'main() { return 0!=1; }'
assert 0 'main() { return 42!=42; }'
assert 0 'int main() { return 0==1; }'
assert 1 'int main() { return 42==42; }'
assert 1 'int main() { return 0!=1; }'
assert 0 'int main() { return 42!=42; }'
assert 1 'main() { return 0<1; }'
assert 0 'main() { return 1<1; }'
assert 0 'main() { return 2<1; }'
assert 1 'main() { return 0<=1; }'
assert 1 'main() { return 1<=1; }'
assert 0 'main() { return 2<=1; }'
assert 1 'int main() { return 0<1; }'
assert 0 'int main() { return 1<1; }'
assert 0 'int main() { return 2<1; }'
assert 1 'int main() { return 0<=1; }'
assert 1 'int main() { return 1<=1; }'
assert 0 'int main() { return 2<=1; }'
assert 1 'main() { return 1>0; }'
assert 0 'main() { return 1>1; }'
assert 0 'main() { return 1>2; }'
assert 1 'main() { return 1>=0; }'
assert 1 'main() { return 1>=1; }'
assert 0 'main() { return 1>=2; }'
assert 1 'int main() { return 1>0; }'
assert 0 'int main() { return 1>1; }'
assert 0 'int main() { return 1>2; }'
assert 1 'int main() { return 1>=0; }'
assert 1 'int main() { return 1>=1; }'
assert 0 'int main() { return 1>=2; }'
assert 3 'main() { a=3; return a; }'
assert 8 'main() { a=3; z=5; return a+z; }'
assert 3 'int main() { int a; a=3; return a; }'
assert 8 'int main() { int a; int z; a=3; z=5; return a+z; }'
assert 3 'int main() { int a=3; return a; }'
assert 8 'int main() { int a=3; int z=5; return a+z; }'
assert 1 'main() { return 1; 2; 3; }'
assert 2 'main() { 1; return 2; 3; }'
assert 3 'main() { 1; 2; return 3; }'
assert 1 'int main() { return 1; 2; 3; }'
assert 2 'int main() { 1; return 2; 3; }'
assert 3 'int main() { 1; 2; return 3; }'
assert 3 'main() { foo=3; return foo; }'
assert 8 'main() { foo123=3; bar=5; return foo123+bar; }'
assert 3 'int main() { int foo=3; return foo; }'
assert 8 'int main() { int foo123=3; int bar=5; return foo123+bar; }'
assert 3 'main() { if (0) return 2; return 3; }'
assert 3 'main() { if (1-1) return 2; return 3; }'
assert 2 'main() { if (1) return 2; return 3; }'
assert 2 'main() { if (2-1) return 2; return 3; }'
assert 3 'int main() { if (0) return 2; return 3; }'
assert 3 'int main() { if (1-1) return 2; return 3; }'
assert 2 'int main() { if (1) return 2; return 3; }'
assert 2 'int main() { if (2-1) return 2; return 3; }'
assert 3 'main() { {1; {2;} return 3;} }'
assert 3 'int main() { {1; {2;} return 3;} }'
assert 10 'main() { i=0; while(i<10) i=i+1; return i; }'
assert 55 'main() { i=0; j=0; while(i<=10) {j=i+j; i=i+1;} return j; }'
assert 10 'int main() { int i=0; i=0; while(i<10) i=i+1; return i; }'
assert 55 'int main() { int i=0; int j=0; while(i<=10) {j=i+j; i=i+1;} return j; }'
assert 55 'main() { i=0; j=0; for (i=0; i<=10; i=i+1) j=i+j; return j; }'
assert 3 'main() { for (;;) return 3; return 5; }'
assert 55 'int main() { int i=0; int j=0; for (i=0; i<=10; i=i+1) j=i+j; return j; }'
assert 3 'int main() { for (;;) return 3; return 5; }'
assert 3 'main() { return ret3(); }'
assert 5 'main() { return ret5(); }'
assert 8 'main() { return add(3, 5); }'
assert 2 'main() { return sub(5, 3); }'
assert 21 'main() { return add6(1,2,3,4,5,6); }'
assert 3 'int main() { return ret3(); }'
assert 5 'int main() { return ret5(); }'
assert 8 'int main() { return add(3, 5); }'
assert 2 'int main() { return sub(5, 3); }'
assert 21 'int main() { return add6(1,2,3,4,5,6); }'
assert 32 'main() { return ret32(); } ret32() { return 32; }'
assert 7 'main() { return add2(3,4); } add2(x,y) { return x+y; }'
assert 1 'main() { return sub2(4,3); } sub2(x,y) { return x-y; }'
assert 55 'main() { return fib(9); } fib(x) { if (x<=1) return 1; return fib(x-1) + fib(x-2); }'
assert 32 'int main() { return ret32(); } int ret32() { return 32; }'
assert 7 'int main() { return add2(3,4); } int add2(int x, int y) { return x+y; }'
assert 1 'int main() { return sub2(4,3); } int sub2(int x, int y) { return x-y; }'
assert 55 'int main() { return fib(9); } int fib(int x) { if (x<=1) return 1; return fib(x-1) + fib(x-2); }'
assert 3 'main() { x=3; return *&x; }'
assert 3 'main() { x=3; y=&x; z=&y; return **z; }'
assert 5 'main() { x=3; y=5; return *(&x+1); }'
assert 3 'main() { x=3; y=5; return *(&y-1); }'
assert 5 'main() { x=3; y=&x; *y=5; return x; }'
assert 7 'main() { x=3; y=5; *(&x+1)=7; return y; }'
assert 7 'main() { x=3; y=5; *(&y-1)=7; return x; }'
assert 2 'main() { x=3; return (&x+2)-&x; }'
assert 3 'int main() { int x=3; return *&x; }'
assert 3 'int main() { int x=3; int *y=&x; int **z=&y; return **z; }'
assert 5 'int main() { int x=3; int y=5; return *(&x+1); }'
assert 5 'int main() { int x=3; int y=5; return *(1+&x); }'
assert 3 'int main() { int x=3; int y=5; return *(&y-1); }'
assert 2 'int main() { int x=3; return (&x+2)-&x; }'
assert 5 'int main() { int x=3; int y=5; int *z=&x; return *(z+1); }'
assert 3 'int main() { int x=3; int y=5; int *z=&y; return *(z-1); }'
assert 5 'int main() { int x=3; int *y=&x; *y=5; return x; }'
assert 7 'int main() { int x=3; int y=5; *(&x+1)=7; return y; }'
assert 7 'int main() { int x=3; int y=5; *(&y-1)=7; return x; }'
assert 8 'int main() { int x=3; int y=5; return foo(&x, y); } int foo(int *x, int y) { return *x + y; }'
echo OK

View File

@ -47,6 +47,14 @@ Token *consume(char *op) {
return t;
}
// Returns true if the current token matches a given string.
Token *peek(char *s) {
if (token->kind != TK_RESERVED || strlen(s) != token->len ||
strncmp(token->str, s, token->len))
return NULL;
return token;
}
// Consumes the current token if it is an identifier.
Token *consume_ident(void) {
if (token->kind != TK_IDENT)
@ -56,11 +64,10 @@ Token *consume_ident(void) {
return t;
}
// Ensure that the current token is `op`.
void expect(char *op) {
if (token->kind != TK_RESERVED || strlen(op) != token->len ||
strncmp(token->str, op, token->len))
error_tok(token, "expected \"%s\"", op);
// Ensure that the current token is a given string
void expect(char *s) {
if (!peek(s))
error_tok(token, "expected \"%s\"", s);
token = token->next;
}
@ -110,7 +117,7 @@ static bool is_alnum(char c) {
static char *starts_with_reserved(char *p) {
// Keyword
static char *kw[] = {"return", "if", "else", "while", "for"};
static char *kw[] = {"return", "if", "else", "while", "for", "int"};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
int len = strlen(kw[i]);

11
type.c
View File

@ -40,7 +40,6 @@ void add_type(Node *node) {
case ND_NE:
case ND_LT:
case ND_LE:
case ND_VAR:
case ND_FUNCALL:
case ND_NUM:
node->ty = int_type;
@ -50,14 +49,16 @@ void add_type(Node *node) {
case ND_ASSIGN:
node->ty = node->lhs->ty;
return;
case ND_VAR:
node->ty = node->var->ty;
return;
case ND_ADDR:
node->ty = pointer_to(node->lhs->ty);
return;
case ND_DEREF:
if (node->lhs->ty->kind == TY_PTR)
node->ty = node->lhs->ty->base;
else
node->ty = int_type;
if (node->lhs->ty->kind != TY_PTR)
error_tok(node->tok, "invalid pointer dereference");
node->ty = node->lhs->ty->base;
return;
}
}