diff --git a/ccc.h b/ccc.h index 075521b..3b19de3 100644 --- a/ccc.h +++ b/ccc.h @@ -87,6 +87,8 @@ typedef enum { ND_BLOCK, // { ... } ND_BREAK, // "break" ND_CONTINUE, // "continue" + ND_GOTO, // "goto" + ND_LABEL, // Labeled statement ND_FUNCALL, // Function call ND_EXPR_STMT, // Expression statement ND_STMT_EXPR, // statement expression @@ -135,6 +137,8 @@ struct Node { // Function call char *funcname; Node *args; + // Goto or labeled statement + char *label_name; Var *var; // Used if kind == ND_VAR long val; // Used if kind == ND_NUM // Struct member access diff --git a/codegen.c b/codegen.c index 47e1f3f..eb7e373 100644 --- a/codegen.c +++ b/codegen.c @@ -263,6 +263,13 @@ void gen(Node *node) { contseq = cont; return; } + case ND_GOTO: + printf(" jmp .L.label.%s.%s\n", funcname, node->label_name); + return; + case ND_LABEL: + printf(".L.label.%s.%s:\n", funcname, node->label_name); + gen(node->lhs); + return; case ND_BLOCK: case ND_STMT_EXPR: for (Node *n = node->body; n; n = n->next) gen(n); diff --git a/parse.c b/parse.c index c124373..e5a4b97 100644 --- a/parse.c +++ b/parse.c @@ -643,6 +643,8 @@ bool is_typename() { // | "{" stmt* "}" // | "break" ";" // | "continue" ";" +// | "goto" ident ";" +// | ident ":" stmt // | declaration // | expr ";" Node *stmt() { @@ -719,6 +721,20 @@ Node *stmt() { expect(";"); return new_node(ND_CONTINUE, tok); } + if (tok = consume("goto")) { + Node *node = new_node(ND_GOTO, tok); + node->label_name = expect_ident(); + expect(";"); + return node; + } + if (tok = consume_ident()) { + if (consume(":")) { + Node *node = new_unary(ND_LABEL, stmt(), tok); + node->label_name = strndup(tok->str, tok->len); + return node; + } + token = tok; + } if (is_typename()) { return declaration(); } diff --git a/tests b/tests index f768f65..ec8fa90 100644 --- a/tests +++ b/tests @@ -793,6 +793,10 @@ int main() { assert(5, ({ int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } j; }), "int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } j;"); assert(11, ({ int i=0; int j=0; while(!i) { while (j++!=10) continue; break; } j; }), "int i=0; int j=0; while(!i) { while (j++!=10) continue; break; } j;"); + assert(3, ({ int i=0; goto a; a: i++; b: i++; c: i++; i; }), "int i=0; goto a; a: i++; b: i++; c: i++; i;"); + assert(2, ({ int i=0; goto e; d: i++; e: i++; f: i++; i; }), "int i=0; goto d; d: i++; e: i++; f: i++; i;"); + assert(1, ({ int i=0; goto i; g: i++; h: i++; i: i++; i; }), "int i=0; goto g; h: i++; i: i++; j: i++; i;"); + printf("OK\n"); return 0; } diff --git a/tokenize.c b/tokenize.c index 41ea6b2..f67ab95 100644 --- a/tokenize.c +++ b/tokenize.c @@ -149,10 +149,10 @@ bool is_alnum(char c) { return is_alpha(c) || ('0' <= c && c <= '9'); } char *starts_with_reserved(char *p) { // Keyword - static char *kw[] = {"return", "if", "else", "while", "for", - "char", "int", "sizeof", "struct", "typedef", - "short", "long", "void", "_Bool", "enum", - "static", "break", "continue"}; + static char *kw[] = {"return", "if", "else", "while", "for", + "char", "int", "sizeof", "struct", "typedef", + "short", "long", "void", "_Bool", "enum", + "static", "break", "continue", "goto"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { int len = strlen(kw[i]); if (startswith(p, kw[i]) && !is_alnum(p[len])) { @@ -284,7 +284,7 @@ Token *tokenize() { continue; } // Single-letter punctuator - if (strchr("+-*/()<>;={},&[].,!~|^", *p)) { + if (strchr("+-*/()<>;={},&[].,!~|^:", *p)) { cur = new_token(TK_RESERVED, cur, p++, 1); continue; }