diff --git a/include/ast.hpp b/include/ast.hpp index 054c4801..ddf3e817 100644 --- a/include/ast.hpp +++ b/include/ast.hpp @@ -94,9 +94,8 @@ struct NullStmtNode : public StmtNode { }; struct IfStmtNode : public StmtNode { - IfStmtNode(std::unique_ptr expr, - std::unique_ptr then, - std::unique_ptr or_else = {}) + IfStmtNode(std::unique_ptr expr, std::unique_ptr then, + std::unique_ptr or_else = {}) : predicate{std::move(expr)}, then{std::move(then)}, or_else{std::move(or_else)} {} @@ -105,8 +104,8 @@ struct IfStmtNode : public StmtNode { virtual void Accept(ModifyingVisitor&) override; std::unique_ptr predicate; - std::unique_ptr then; - std::unique_ptr or_else; + std::unique_ptr then; + std::unique_ptr or_else; }; struct ReturnStmtNode : public StmtNode { diff --git a/parser.y b/parser.y index 9e57f083..cf919135 100644 --- a/parser.y +++ b/parser.y @@ -66,6 +66,23 @@ extern std::unique_ptr program; %left '+' '-' %left '*' '/' '%' +// Resolve the ambiguity in the "dangling-else" grammar. +// Example: IF '(' expr ')' IF '(' expr ')' stmt • ELSE stmt +// Yacc has two options to make, either shift or reduce: +// Shift derivation +// stmt +// ↳ 13: IF '(' expr ')' stmt +// ↳ 14: IF '(' expr ')' stmt • ELSE stmt +// Reduce derivation +// stmt +// ↳ 14: IF '(' expr ')' stmt ELSE stmt +// ↳ 13: IF '(' expr ')' stmt • +// +// Our goal is to find the closest IF for ELSE, so we tell Yacc to shift. +// ELSE has a higher precendence than IF due to increasing precedence order. +%precedence IF +%precedence ELSE + %start entry %% @@ -74,12 +91,12 @@ entry: main_func { } ; - /* TODO: mix declarations and statements in compound statement */ main_func: INT MAIN '(' ')' block { $$ = $5; } ; + /* TODO: mix declarations and statements in compound statement */ block: '{' decls stmts '}' { $$ = std::make_unique($2, $3); } @@ -109,8 +126,9 @@ stmts: stmts stmt { stmt: ';' { $$ = std::make_unique(); } | RETURN expr ';' { $$ = std::make_unique($2); } | expr ';' { $$ = std::make_unique($1); } - | IF '(' expr ')' block { $$ = std::make_unique($3, $5); } - | IF '(' expr ')' block ELSE block { $$ = std::make_unique($3, $5, $7); } + | block { $$ = $1; } + | IF '(' expr ')' stmt %prec IF { $$ = std::make_unique($3, $5); } + | IF '(' expr ')' stmt ELSE stmt { $$ = std::make_unique($3, $5, $7); } ; expr: ID { $$ = std::make_unique($1); }