diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-06-08 17:00:13 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-06-08 17:00:13 +0800 |
| commit | c2995a5b95a1e04620ad1fc94ed19186234388ca (patch) | |
| tree | d64317919e6af0698ef14ce0d0d635aae5c5a415 | |
| parent | cc717e35b0d361a4e894955cf87d62fffc1a176b (diff) | |
| download | rcc-project-c2995a5b95a1e04620ad1fc94ed19186234388ca.tar.gz rcc-project-c2995a5b95a1e04620ad1fc94ed19186234388ca.tar.xz | |
[legacy/frontend] Add lexer, parser, and AST
| -rw-r--r-- | rcc-legacy/src/frontend/ast.cc | 214 | ||||
| -rw-r--r-- | rcc-legacy/src/frontend/lexer.l | 95 | ||||
| -rw-r--r-- | rcc-legacy/src/frontend/parser.y | 686 |
3 files changed, 995 insertions, 0 deletions
diff --git a/rcc-legacy/src/frontend/ast.cc b/rcc-legacy/src/frontend/ast.cc new file mode 100644 index 0000000..c82272a --- /dev/null +++ b/rcc-legacy/src/frontend/ast.cc @@ -0,0 +1,214 @@ +/* Source code for RCC-Legacy. + Copyright (C) 2026 Richard Qin + +This file is part of RCC. + +RCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +RCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with RCC; see the file LICENSE. If not see +<http://www.gnu.org/licenses/>. */ + +/// @file ast.cc +/// @brief AST debug printing and built-in function registration. + +#include "rcc/ast.h" + +#include "rcc/symtab.h" + +#include <cstdio> + +namespace rcc { + +// AST debug printing + +static void indent(int n) { + for (int i = 0; i < n; i++) + fprintf(stderr, " "); +} + +void ast_dump(const ASTNode* node, int depth) { + if (!node) { + indent(depth); + fprintf(stderr, "(null)\n"); + return; + } + + indent(depth); + switch (node->kind) { + case NodeKind::CompUnit: + fprintf(stderr, "CompUnit\n"); + for (auto& c : static_cast<const CompUnitNode*>(node)->decls_and_funcs) + ast_dump(c.get(), depth + 1); + break; + + case NodeKind::FuncDef: { + auto* f = static_cast<const FuncDefNode*>(node); + fprintf(stderr, "FuncDef '%s' -> %s\n", f->name.c_str(), btype_str(f->ret_type)); + for (auto& p : f->params) + ast_dump(p.get(), depth + 1); + ast_dump(f->body.get(), depth + 1); + break; + } + + case NodeKind::FuncFParam: { + auto* p = static_cast<const FuncFParamNode*>(node); + fprintf(stderr, "Param '%s' : %s%s\n", p->name.c_str(), btype_str(p->btype), + p->is_array ? "[]" : ""); + break; + } + + case NodeKind::VarDecl: { + auto* d = static_cast<const VarDeclNode*>(node); + fprintf(stderr, "%s %s\n", d->is_const ? "ConstDecl" : "VarDecl", btype_str(d->btype)); + for (auto& def : d->defs) + ast_dump(def.get(), depth + 1); + break; + } + + case NodeKind::VarDef: { + auto* d = static_cast<const VarDefNode*>(node); + fprintf(stderr, "VarDef '%s'", d->name.c_str()); + if (!d->dims.empty()) + fprintf(stderr, " (array, %zu dims)", d->dims.size()); + fprintf(stderr, "\n"); + if (d->init_val) + ast_dump(d->init_val.get(), depth + 1); + break; + } + + case NodeKind::InitList: + fprintf(stderr, "InitList\n"); + for (auto& i : static_cast<const InitListNode*>(node)->items) + ast_dump(i.get(), depth + 1); + break; + + case NodeKind::Block: + fprintf(stderr, "Block\n"); + for (auto& i : static_cast<const BlockNode*>(node)->items) + ast_dump(i.get(), depth + 1); + break; + + case NodeKind::AssignStmt: + fprintf(stderr, "Assign\n"); + ast_dump(static_cast<const AssignStmtNode*>(node)->lval.get(), depth + 1); + ast_dump(static_cast<const AssignStmtNode*>(node)->expr.get(), depth + 1); + break; + + case NodeKind::ExprStmt: + fprintf(stderr, "ExprStmt\n"); + if (static_cast<const ExprStmtNode*>(node)->expr) + ast_dump(static_cast<const ExprStmtNode*>(node)->expr.get(), depth + 1); + break; + + case NodeKind::IfStmt: { + auto* s = static_cast<const IfStmtNode*>(node); + fprintf(stderr, "If\n"); + ast_dump(s->cond.get(), depth + 1); + ast_dump(s->then_stmt.get(), depth + 1); + if (s->else_stmt) + ast_dump(s->else_stmt.get(), depth + 1); + break; + } + + case NodeKind::WhileStmt: + fprintf(stderr, "While\n"); + ast_dump(static_cast<const WhileStmtNode*>(node)->cond.get(), depth + 1); + ast_dump(static_cast<const WhileStmtNode*>(node)->body.get(), depth + 1); + break; + + case NodeKind::BreakStmt: + fprintf(stderr, "Break\n"); + break; + case NodeKind::ContinueStmt: + fprintf(stderr, "Continue\n"); + break; + + case NodeKind::ReturnStmt: + fprintf(stderr, "Return\n"); + if (static_cast<const ReturnStmtNode*>(node)->expr) + ast_dump(static_cast<const ReturnStmtNode*>(node)->expr.get(), depth + 1); + break; + + case NodeKind::IntLiteral: + fprintf(stderr, "IntLit %d\n", static_cast<const IntLiteralNode*>(node)->value); + break; + + case NodeKind::FloatLiteral: + fprintf(stderr, "FloatLit %f\n", + static_cast<double>(static_cast<const FloatLiteralNode*>(node)->value)); + break; + + case NodeKind::LVal: { + auto* l = static_cast<const LValNode*>(node); + fprintf(stderr, "LVal '%s'\n", l->name.c_str()); + for (auto& idx : l->indices) + ast_dump(idx.get(), depth + 1); + break; + } + + case NodeKind::FuncCall: { + auto* c = static_cast<const FuncCallNode*>(node); + fprintf(stderr, "Call '%s'\n", c->name.c_str()); + for (auto& a : c->args) + ast_dump(a.get(), depth + 1); + break; + } + + case NodeKind::BinaryExpr: { + auto* e = static_cast<const BinaryExprNode*>(node); + const char* ops[] = {"Add", "Sub", "Mul", "Div", "Mod", "Lt", "Gt", + "Le", "Ge", "Eq", "Ne", "And", "Or"}; + fprintf(stderr, "BinExpr %s\n", ops[static_cast<int>(e->op)]); + ast_dump(e->lhs.get(), depth + 1); + ast_dump(e->rhs.get(), depth + 1); + break; + } + + case NodeKind::UnaryExpr: { + auto* e = static_cast<const UnaryExprNode*>(node); + const char* ops[] = {"Pos", "Neg", "Not"}; + fprintf(stderr, "UnaryExpr %s\n", ops[static_cast<int>(e->op)]); + ast_dump(e->operand.get(), depth + 1); + break; + } + + case NodeKind::ImplicitCast: { + auto* c = static_cast<const ImplicitCastNode*>(node); + fprintf(stderr, "ImplicitCast -> %s\n", btype_str(c->target_type)); + ast_dump(c->operand.get(), depth + 1); + break; + } + } +} + +// built-in functions + +void SymbolTable::register_builtins() { + // I/O functions + register_func({"getint", BType::Int, {}}); + register_func({"getch", BType::Int, {}}); + register_func({"getfloat", BType::Float, {}}); + register_func({"getarray", BType::Int, {{BType::Int, true}}}); + register_func({"getfarray", BType::Int, {{BType::Float, true}}}); + register_func({"putint", BType::Void, {{BType::Int, false}}}); + register_func({"putch", BType::Void, {{BType::Int, false}}}); + register_func({"putfloat", BType::Void, {{BType::Float, false}}}); + register_func({"putarray", BType::Void, {{BType::Int, false}, {BType::Int, true}}}); + register_func({"putfarray", BType::Void, {{BType::Int, false}, {BType::Float, true}}}); + register_func({"putf", BType::Void, {}}); // variable parameters + + // timing functions (actually call _sysy_starttime / _sysy_stoptime) + register_func({"starttime", BType::Void, {}}); + register_func({"stoptime", BType::Void, {}}); +} + +} // namespace rcc diff --git a/rcc-legacy/src/frontend/lexer.l b/rcc-legacy/src/frontend/lexer.l new file mode 100644 index 0000000..33359ef --- /dev/null +++ b/rcc-legacy/src/frontend/lexer.l @@ -0,0 +1,95 @@ +/* Source code for RCC-Legacy. + Copyright (C) 2026 Richard Qin + +This file is part of RCC. + +RCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +RCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with RCC; see the file LICENSE. If not see +<http://www.gnu.org/licenses/>. */ + +%{ +/* lexer.l - SysY2022 Lexer */ +#include "rcc/ast.h" +#include "parser.tab.h" + +#include <cstdlib> +#include <cstring> + +// Line number tracking +int yylineno_val = 1; +#define YY_USER_ACTION yylloc.first_line = yylineno_val; + +using namespace rcc; +%} + +%option noyywrap nounput noinput + +/* Exclusive start condition: multi-line comments */ +%x COMMENT + +%% + + /* ─── Whitespace and Newlines ─── */ +[ \t\r]+ { /* skip whitespace */ } +\n { yylineno_val++; } + + /* ─── Comments ─── */ +"//"[^\n]* { /* single-line comment */ } +"/*" { BEGIN(COMMENT); } +<COMMENT>"*/" { BEGIN(INITIAL); } +<COMMENT>\n { yylineno_val++; } +<COMMENT>. { /* eat anything inside comment */ } + + /* ─── Keywords ─── */ +"int" { return T_INT; } +"float" { return T_FLOAT; } +"void" { return T_VOID; } +"const" { return T_CONST; } +"if" { return T_IF; } +"else" { return T_ELSE; } +"while" { return T_WHILE; } +"break" { return T_BREAK; } +"continue" { return T_CONTINUE; } +"return" { return T_RETURN; } + + /* ─── Integer Constants (Hex > Octal > Decimal, order is important) ─── */ +0[xX][0-9a-fA-F]+ { yylval.int_val = static_cast<int32_t>(strtoul(yytext, nullptr, 16)); return INT_CONST; } +0[0-7]+ { yylval.int_val = static_cast<int32_t>(strtoul(yytext, nullptr, 8)); return INT_CONST; } +[0-9]+ { yylval.int_val = static_cast<int32_t>(strtoul(yytext, nullptr, 10)); return INT_CONST; } + + /* ─── Floating Point Constants (Hex float + Decimal float) ─── */ +0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9]+)? { yylval.float_val = strtof(yytext, nullptr); return FLOAT_CONST; } +0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*([pP][+-]?[0-9]+)? { yylval.float_val = strtof(yytext, nullptr); return FLOAT_CONST; } +0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+ { yylval.float_val = strtof(yytext, nullptr); return FLOAT_CONST; } +[0-9]*\.[0-9]+([eE][+-]?[0-9]+)? { yylval.float_val = strtof(yytext, nullptr); return FLOAT_CONST; } +[0-9]+\.[0-9]*([eE][+-]?[0-9]+)? { yylval.float_val = strtof(yytext, nullptr); return FLOAT_CONST; } +[0-9]+[eE][+-]?[0-9]+ { yylval.float_val = strtof(yytext, nullptr); return FLOAT_CONST; } + + /* ─── Identifiers ─── */ +[a-zA-Z_][a-zA-Z0-9_]* { yylval.str_val = strdup(yytext); return IDENT; } + + /* ─── Multi-character Operators ─── */ +"&&" { return T_AND; } +"||" { return T_OR; } +"<=" { return T_LE; } +">=" { return T_GE; } +"==" { return T_EQ; } +"!=" { return T_NE; } + + /* ─── Format Strings (for putf only) ─── */ +\"[^\"]*\" { yylval.str_val = strdup(yytext); return STRING_CONST; } + + /* ─── Single-character Operators and Delimiters, return ASCII directly ─── */ +. { return yytext[0]; } + +%% diff --git a/rcc-legacy/src/frontend/parser.y b/rcc-legacy/src/frontend/parser.y new file mode 100644 index 0000000..b2d5506 --- /dev/null +++ b/rcc-legacy/src/frontend/parser.y @@ -0,0 +1,686 @@ +/* Source code for RCC-Legacy. + Copyright (C) 2026 Richard Qin + +This file is part of RCC. + +RCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +RCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with RCC; see the file LICENSE. If not see +<http://www.gnu.org/licenses/>. */ +%{ +/* parser.y - SysY2022 Parser */ +#include "rcc/ast.h" + +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <string> +#include <vector> + +using namespace rcc; + +// Global AST root node +extern CompUnitNode* g_ast_root; + +extern int yylex(); +extern int yylineno_val; +void yyerror(const char* msg); +extern FILE* yyin; +%} + +/* ─── Bison Configuration ─── */ +%locations +%define parse.error verbose + +/* ─── Value Types ─── */ +%union { + int32_t int_val; + float float_val; + char* str_val; + rcc::ASTNode* node; + rcc::ASTList* node_list; + rcc::BType btype; +} + +/* ─── Terminals ─── */ +%token <int_val> INT_CONST +%token <float_val> FLOAT_CONST +%token <str_val> IDENT STRING_CONST + +%token T_INT T_FLOAT T_VOID T_CONST +%token T_IF T_ELSE T_WHILE T_BREAK T_CONTINUE T_RETURN +%token T_AND T_OR T_LE T_GE T_EQ T_NE + +/* ─── Non-terminal Types ─── */ +%type <node> CompUnit +%type <node> GlobalItem FuncDef_Void +%type <node> FuncFParam +%type <node> Block BlockItem Stmt +%type <node> Decl ConstDecl VarDecl ConstDef VarDef +%type <node> InitVal ConstInitVal +%type <node> Exp Cond LVal PrimaryExp UnaryExp Number +%type <node> MulExp AddExp RelExp EqExp LAndExp LOrExp ConstExp +%type <node_list> ConstDefList VarDefList +%type <node_list> FuncFParams FuncRParams +%type <node_list> BlockItemList +%type <node_list> ArrayDims ArrayIndices +%type <node_list> InitValList ConstInitValList +%type <btype> BType + +/* ─── Resolve dangling else ─── */ +%nonassoc LOWER_THAN_ELSE +%nonassoc T_ELSE + +/* ─── Start Symbol ─── */ +%start CompUnit + +%% + +/* Compilation Unit */ + +CompUnit + : CompUnit GlobalItem { + auto* unit = static_cast<CompUnitNode*>($1); + unit->decls_and_funcs.push_back(ASTPtr($2)); + $$ = unit; + } + | CompUnit ConstDecl { + auto* unit = static_cast<CompUnitNode*>($1); + unit->decls_and_funcs.push_back(ASTPtr($2)); + $$ = unit; + } + | CompUnit FuncDef_Void { + auto* unit = static_cast<CompUnitNode*>($1); + unit->decls_and_funcs.push_back(ASTPtr($2)); + $$ = unit; + } + | GlobalItem { + auto* unit = new CompUnitNode(); + unit->decls_and_funcs.push_back(ASTPtr($1)); + $$ = unit; + g_ast_root = unit; + } + | ConstDecl { + auto* unit = new CompUnitNode(); + unit->decls_and_funcs.push_back(ASTPtr($1)); + $$ = unit; + g_ast_root = unit; + } + | FuncDef_Void { + auto* unit = new CompUnitNode(); + unit->decls_and_funcs.push_back(ASTPtr($1)); + $$ = unit; + g_ast_root = unit; + } + ; + +/* GlobalItem */ +GlobalItem + : BType IDENT '(' ')' Block { + /* FuncDef: int/float foo() { ... } */ + auto* func = new FuncDefNode(); + func->ret_type = $1; + func->name = $2; free($2); + func->body = ASTPtr($5); + func->lineno = @1.first_line; + $$ = func; + } + | BType IDENT '(' FuncFParams ')' Block { + /* FuncDef: int/float foo(params) { ... } */ + auto* func = new FuncDefNode(); + func->ret_type = $1; + func->name = $2; free($2); + func->params = std::move(*$4); delete $4; + func->body = ASTPtr($6); + func->lineno = @1.first_line; + $$ = func; + } + | BType IDENT ArrayDims ';' { + /* VarDecl: int x; / int x[10]; */ + auto* decl = new VarDeclNode(); + decl->is_const = false; + decl->btype = $1; + auto* def = new VarDefNode(); + def->name = $2; free($2); + def->dims = std::move(*$3); delete $3; + def->lineno = @2.first_line; + decl->defs.push_back(ASTPtr(def)); + $$ = decl; + } + | BType IDENT ArrayDims '=' InitVal ';' { + /* VarDecl: int x = 5; / int x[10] = {1,2}; */ + auto* decl = new VarDeclNode(); + decl->is_const = false; + decl->btype = $1; + auto* def = new VarDefNode(); + def->name = $2; free($2); + def->dims = std::move(*$3); delete $3; + def->init_val = ASTPtr($5); + def->lineno = @2.first_line; + decl->defs.push_back(ASTPtr(def)); + $$ = decl; + } + | BType IDENT ArrayDims ',' VarDefList ';' { + /* VarDecl: int x, y, z; */ + auto* decl = new VarDeclNode(); + decl->is_const = false; + decl->btype = $1; + auto* def = new VarDefNode(); + def->name = $2; free($2); + def->dims = std::move(*$3); delete $3; + def->lineno = @2.first_line; + decl->defs.push_back(ASTPtr(def)); + for (auto& d : *$5) decl->defs.push_back(std::move(d)); + delete $5; + $$ = decl; + } + | BType IDENT ArrayDims '=' InitVal ',' VarDefList ';' { + /* VarDecl: int x = 1, y, z; */ + auto* decl = new VarDeclNode(); + decl->is_const = false; + decl->btype = $1; + auto* def = new VarDefNode(); + def->name = $2; free($2); + def->dims = std::move(*$3); delete $3; + def->init_val = ASTPtr($5); + def->lineno = @2.first_line; + decl->defs.push_back(ASTPtr(def)); + for (auto& d : *$7) decl->defs.push_back(std::move(d)); + delete $7; + $$ = decl; + } + ; + +/* void functions */ +FuncDef_Void + : T_VOID IDENT '(' ')' Block { + auto* func = new FuncDefNode(); + func->ret_type = BType::Void; + func->name = $2; free($2); + func->body = ASTPtr($5); + func->lineno = @1.first_line; + $$ = func; + } + | T_VOID IDENT '(' FuncFParams ')' Block { + auto* func = new FuncDefNode(); + func->ret_type = BType::Void; + func->name = $2; free($2); + func->params = std::move(*$4); delete $4; + func->body = ASTPtr($6); + func->lineno = @1.first_line; + $$ = func; + } + ; + +/* Types */ + +BType : T_INT { $$ = BType::Int; } + | T_FLOAT { $$ = BType::Float; } + ; + +/* Declarations */ + +/* Decl */ +Decl : ConstDecl { $$ = $1; } + | VarDecl { $$ = $1; } + ; + +ConstDecl + : T_CONST BType ConstDefList ';' { + auto* decl = new VarDeclNode(); + decl->is_const = true; + decl->btype = $2; + decl->defs = std::move(*$3); + delete $3; + $$ = decl; + } + ; + +ConstDefList + : ConstDef { + $$ = new ASTList(); + $$->push_back(ASTPtr($1)); + } + | ConstDefList ',' ConstDef { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +ConstDef + : IDENT ArrayDims '=' ConstInitVal { + auto* def = new VarDefNode(); + def->name = $1; free($1); + def->dims = std::move(*$2); delete $2; + def->init_val = ASTPtr($4); + def->lineno = @1.first_line; + $$ = def; + } + ; + +ConstInitVal + : ConstExp { $$ = $1; } + | '{' '}' { + $$ = new InitListNode(); + } + | '{' ConstInitValList '}' { + auto* list = new InitListNode(); + list->items = std::move(*$2); delete $2; + $$ = list; + } + ; + +ConstInitValList + : ConstInitVal { + $$ = new ASTList(); + $$->push_back(ASTPtr($1)); + } + | ConstInitValList ',' ConstInitVal { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +/* Local VarDecl */ +VarDecl + : BType VarDefList ';' { + auto* decl = new VarDeclNode(); + decl->is_const = false; + decl->btype = $1; + decl->defs = std::move(*$2); + delete $2; + $$ = decl; + } + ; + +VarDefList + : VarDef { + $$ = new ASTList(); + $$->push_back(ASTPtr($1)); + } + | VarDefList ',' VarDef { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +VarDef + : IDENT ArrayDims { + auto* def = new VarDefNode(); + def->name = $1; free($1); + def->dims = std::move(*$2); delete $2; + def->lineno = @1.first_line; + $$ = def; + } + | IDENT ArrayDims '=' InitVal { + auto* def = new VarDefNode(); + def->name = $1; free($1); + def->dims = std::move(*$2); delete $2; + def->init_val = ASTPtr($4); + def->lineno = @1.first_line; + $$ = def; + } + ; + +InitVal + : Exp { $$ = $1; } + | '{' '}' { + $$ = new InitListNode(); + } + | '{' InitValList '}' { + auto* list = new InitListNode(); + list->items = std::move(*$2); delete $2; + $$ = list; + } + ; + +InitValList + : InitVal { + $$ = new ASTList(); + $$->push_back(ASTPtr($1)); + } + | InitValList ',' InitVal { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +/* Array Dimensions */ +ArrayDims + : /* empty */ { $$ = new ASTList(); } + | ArrayDims '[' ConstExp ']' { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +/* Function Parameters */ + +FuncFParams + : FuncFParam { + $$ = new ASTList(); + $$->push_back(ASTPtr($1)); + } + | FuncFParams ',' FuncFParam { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +FuncFParam + : BType IDENT { + auto* p = new FuncFParamNode(); + p->btype = $1; + p->name = $2; free($2); + p->lineno = @1.first_line; + $$ = p; + } + | BType IDENT '[' ']' ArrayDims { + /* Array parameter */ + auto* p = new FuncFParamNode(); + p->btype = $1; + p->name = $2; free($2); + p->is_array = true; + p->dims = std::move(*$5); delete $5; + p->lineno = @1.first_line; + $$ = p; + } + ; + +/* Blocks and Statements */ + +Block + : '{' BlockItemList '}' { + auto* block = new BlockNode(); + block->items = std::move(*$2); delete $2; + $$ = block; + } + ; + +BlockItemList + : /* empty */ { $$ = new ASTList(); } + | BlockItemList BlockItem { + $1->push_back(ASTPtr($2)); + $$ = $1; + } + ; + +BlockItem + : Decl { $$ = $1; } + | Stmt { $$ = $1; } + ; + +Stmt + : LVal '=' Exp ';' { + auto* s = new AssignStmtNode(); + s->lval = ASTPtr($1); + s->expr = ASTPtr($3); + s->lineno = @1.first_line; + $$ = s; + } + | Exp ';' { + auto* s = new ExprStmtNode(); + s->expr = ASTPtr($1); + $$ = s; + } + | ';' { + $$ = new ExprStmtNode(); + } + | Block { $$ = $1; } + | T_IF '(' Cond ')' Stmt %prec LOWER_THAN_ELSE { + auto* s = new IfStmtNode(); + s->cond = ASTPtr($3); + s->then_stmt = ASTPtr($5); + s->lineno = @1.first_line; + $$ = s; + } + | T_IF '(' Cond ')' Stmt T_ELSE Stmt { + auto* s = new IfStmtNode(); + s->cond = ASTPtr($3); + s->then_stmt = ASTPtr($5); + s->else_stmt = ASTPtr($7); + s->lineno = @1.first_line; + $$ = s; + } + | T_WHILE '(' Cond ')' Stmt { + auto* s = new WhileStmtNode(); + s->cond = ASTPtr($3); + s->body = ASTPtr($5); + s->lineno = @1.first_line; + $$ = s; + } + | T_BREAK ';' { + $$ = new BreakStmtNode(); + $$->lineno = @1.first_line; + } + | T_CONTINUE ';' { + $$ = new ContinueStmtNode(); + $$->lineno = @1.first_line; + } + | T_RETURN ';' { + $$ = new ReturnStmtNode(); + $$->lineno = @1.first_line; + } + | T_RETURN Exp ';' { + auto* s = new ReturnStmtNode(); + s->expr = ASTPtr($2); + s->lineno = @1.first_line; + $$ = s; + } + ; + +/* Expressions */ + +/* Exp relaxed to LOrExp */ +Exp : LOrExp { $$ = $1; } ; + +Cond : LOrExp { $$ = $1; } ; + +ConstExp : AddExp { $$ = $1; } ; + +LVal + : IDENT ArrayIndices { + auto* lv = new LValNode(); + lv->name = $1; free($1); + lv->indices = std::move(*$2); delete $2; + lv->lineno = @1.first_line; + $$ = lv; + } + ; + +ArrayIndices + : /* empty */ { $$ = new ASTList(); } + | ArrayIndices '[' Exp ']' { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + ; + +PrimaryExp + : '(' Exp ')' { $$ = $2; } + | LVal { $$ = $1; } + | Number { $$ = $1; } + ; + +Number + : INT_CONST { + auto* n = new IntLiteralNode(); + n->value = $1; + n->lineno = @1.first_line; + $$ = n; + } + | FLOAT_CONST { + auto* n = new FloatLiteralNode(); + n->value = $1; + n->lineno = @1.first_line; + $$ = n; + } + ; + +UnaryExp + : PrimaryExp { $$ = $1; } + | IDENT '(' ')' { + auto* c = new FuncCallNode(); + c->name = $1; free($1); + c->lineno = @1.first_line; + $$ = c; + } + | IDENT '(' FuncRParams ')' { + auto* c = new FuncCallNode(); + c->name = $1; free($1); + c->args = std::move(*$3); delete $3; + c->lineno = @1.first_line; + $$ = c; + } + | '+' UnaryExp { + auto* u = new UnaryExprNode(); + u->op = UnaryOp::Pos; + u->operand = ASTPtr($2); + u->lineno = @1.first_line; + $$ = u; + } + | '-' UnaryExp { + auto* u = new UnaryExprNode(); + u->op = UnaryOp::Neg; + u->operand = ASTPtr($2); + u->lineno = @1.first_line; + $$ = u; + } + | '!' UnaryExp { + auto* u = new UnaryExprNode(); + u->op = UnaryOp::Not; + u->operand = ASTPtr($2); + u->lineno = @1.first_line; + $$ = u; + } + ; + +FuncRParams + : Exp { + $$ = new ASTList(); + $$->push_back(ASTPtr($1)); + } + | FuncRParams ',' Exp { + $1->push_back(ASTPtr($3)); + $$ = $1; + } + /* putf format string parameter */ + | STRING_CONST { + $$ = new ASTList(); + auto* n = new IntLiteralNode(); /* placeholder */ + n->value = 0; + $$->push_back(ASTPtr(static_cast<ASTNode*>(n))); + free($1); + } + | FuncRParams ',' STRING_CONST { + free($3); + $$ = $1; + } + ; + +MulExp + : UnaryExp { $$ = $1; } + | MulExp '*' UnaryExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Mul; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | MulExp '/' UnaryExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Div; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | MulExp '%' UnaryExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Mod; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + ; + +AddExp + : MulExp { $$ = $1; } + | AddExp '+' MulExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Add; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | AddExp '-' MulExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Sub; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + ; + +RelExp + : AddExp { $$ = $1; } + | RelExp '<' AddExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Lt; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | RelExp '>' AddExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Gt; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | RelExp T_LE AddExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Le; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | RelExp T_GE AddExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Ge; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + ; + +EqExp + : RelExp { $$ = $1; } + | EqExp T_EQ RelExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Eq; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + | EqExp T_NE RelExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Ne; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + ; + +LAndExp + : EqExp { $$ = $1; } + | LAndExp T_AND EqExp { + auto* e = new BinaryExprNode(); e->op = BinOp::And; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + ; + +LOrExp + : LAndExp { $$ = $1; } + | LOrExp T_OR LAndExp { + auto* e = new BinaryExprNode(); e->op = BinOp::Or; + e->lhs = ASTPtr($1); e->rhs = ASTPtr($3); + e->lineno = @2.first_line; $$ = e; + } + ; + +%% + +/* Global Variables and Error Handling */ +CompUnitNode* g_ast_root = nullptr; + +void yyerror(const char* msg) { + fprintf(stderr, "rcc: parse error (line %d): %s\n", yylineno_val, msg); +} |
