aboutsummaryrefslogtreecommitdiff
path: root/rcc-legacy/src
diff options
context:
space:
mode:
Diffstat (limited to 'rcc-legacy/src')
-rw-r--r--rcc-legacy/src/frontend/ast.cc214
-rw-r--r--rcc-legacy/src/frontend/lexer.l95
-rw-r--r--rcc-legacy/src/frontend/parser.y686
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);
+}