/* 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
. */
%{
/* parser.y - SysY2022 Parser */
#include "rcc/ast.h"
#include
#include
#include
#include
#include
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_CONST
%token FLOAT_CONST
%token 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 CompUnit
%type GlobalItem FuncDef_Void
%type FuncFParam
%type Block BlockItem Stmt
%type Decl ConstDecl VarDecl ConstDef VarDef
%type InitVal ConstInitVal
%type Exp Cond LVal PrimaryExp UnaryExp Number
%type MulExp AddExp RelExp EqExp LAndExp LOrExp ConstExp
%type ConstDefList VarDefList
%type FuncFParams FuncRParams
%type BlockItemList
%type ArrayDims ArrayIndices
%type InitValList ConstInitValList
%type BType
/* ─── Resolve dangling else ─── */
%nonassoc LOWER_THAN_ELSE
%nonassoc T_ELSE
/* ─── Start Symbol ─── */
%start CompUnit
%%
/* Compilation Unit */
CompUnit
: CompUnit GlobalItem {
auto* unit = static_cast($1);
unit->decls_and_funcs.push_back(ASTPtr($2));
$$ = unit;
}
| CompUnit ConstDecl {
auto* unit = static_cast($1);
unit->decls_and_funcs.push_back(ASTPtr($2));
$$ = unit;
}
| CompUnit FuncDef_Void {
auto* unit = static_cast($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(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);
}