parser start

This commit is contained in:
bronku 2025-11-27 15:45:10 +01:00
parent 7fecbd04b3
commit c76b84682a
14 changed files with 589 additions and 21 deletions

7
src/ast.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "ast/string_utils.hpp"
#include "ast/base.hpp"
#include "ast/expressions.hpp"
#include "ast/literals.hpp"
#include "ast/operators.hpp"
#include "ast/statements.hpp"

15
src/ast/base.hpp Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include <string>
struct Node
{
virtual ~Node() = default;
virtual std::string dump(int indent = 0) const = 0;
};
struct Expr : Node
{
};
struct Stmt : Node
{
};

145
src/ast/expressions.hpp Normal file
View file

@ -0,0 +1,145 @@
#pragma once
#include <sstream>
#include "base.hpp"
#include "operators.hpp"
#include "literals.hpp"
template <typename T>
using ptr = std::unique_ptr<T>;
struct BinOp : Expr
{
BinaryOperator op;
ptr<Expr> left;
ptr<Expr> right;
BinOp(BinaryOperator o, ptr<Expr> l, ptr<Expr> r)
: op(o), left(std::move(l)), right(std::move(r)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "BinOp(" << op.symbol() << ")\n";
oss << left->dump(indent + 1) << "\n";
oss << right->dump(indent + 1);
return oss.str();
}
};
struct UnaryOpExpr : Expr
{
UnaryOperator op;
ptr<Expr> operand;
UnaryOpExpr(UnaryOperator o, ptr<Expr> expr)
: op(o), operand(std::move(expr)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "UnaryOp(" << op.symbol() << ")\n";
oss << operand->dump(indent + 1);
return oss.str();
}
};
struct Call : Expr
{
ptr<Expr> func;
std::vector<ptr<Expr>> args;
Call(ptr<Expr> f, std::vector<ptr<Expr>> a)
: func(std::move(f)), args(std::move(a)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "Call\n";
oss << func->dump(indent + 1);
for (const auto &arg : args)
{
oss << "\n"
<< arg->dump(indent + 1);
}
return oss.str();
}
};
struct Attribute : Expr
{
ptr<Expr> value;
std::string attr;
Attribute(ptr<Expr> v, std::string a)
: value(std::move(v)), attr(a) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "Attribute(." << attr << ")\n";
oss << value->dump(indent + 1);
return oss.str();
}
};
struct Subscript : Expr
{
ptr<Expr> value;
ptr<Expr> index;
Subscript(ptr<Expr> v, ptr<Expr> i)
: value(std::move(v)), index(std::move(i)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "Subscript\n";
oss << value->dump(indent + 1) << "\n";
oss << index->dump(indent + 1);
return oss.str();
}
};
struct List : Expr
{
std::vector<ptr<Expr>> elements;
List(std::vector<ptr<Expr>> elems)
: elements(std::move(elems)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "List";
for (const auto &elem : elements)
{
oss << "\n"
<< elem->dump(indent + 1);
}
return oss.str();
}
};
struct Dict : Expr
{
std::vector<std::pair<ptr<Expr>, ptr<Expr>>> pairs;
Dict(std::vector<std::pair<ptr<Expr>, ptr<Expr>>> p)
: pairs(std::move(p)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "Dict";
for (const auto &[key, val] : pairs)
{
oss << "\n"
<< indent_str(indent + 1) << "Pair";
oss << "\n"
<< key->dump(indent + 2);
oss << "\n"
<< val->dump(indent + 2);
}
return oss.str();
}
};

39
src/ast/literals.hpp Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "base.hpp"
#include "string_utils.hpp"
struct Identifier : Expr
{
std::string name;
Identifier(std::string n) : name(n) {}
std::string dump(int indent = 0) const override
{
return indent_str(indent) + "Identifier(" + name + ")";
}
};
struct Number : Expr
{
std::string value;
Number(std::string v) : value(v) {}
std::string dump(int indent = 0) const override
{
return indent_str(indent) + "Number(" + value + ")";
}
};
struct String : Expr
{
std::string value;
String(std::string v) : value(v) {}
std::string dump(int indent = 0) const override
{
return indent_str(indent) + "String(\"" + value + "\")";
}
};

105
src/ast/operators.hpp Normal file
View file

@ -0,0 +1,105 @@
#pragma once
#include <string>
struct BinaryOperator
{
enum Kind
{
ADD,
SUB,
MUL,
DIV,
MOD,
EQ,
NE,
LT,
LTE,
GT,
GTE,
AND,
OR,
BIT_AND,
BIT_OR,
BIT_XOR,
LSHIFT,
RSHIFT
};
Kind kind;
BinaryOperator(Kind k) : kind(k) {}
std::string symbol() const
{
switch (kind)
{
case ADD:
return "+";
case SUB:
return "-";
case MUL:
return "*";
case DIV:
return "/";
case MOD:
return "%";
case EQ:
return "==";
case NE:
return "!=";
case LT:
return "<";
case LTE:
return "<=";
case GT:
return ">";
case GTE:
return ">=";
case AND:
return "and";
case OR:
return "or";
case BIT_AND:
return "&";
case BIT_OR:
return "|";
case BIT_XOR:
return "^";
case LSHIFT:
return "<<";
case RSHIFT:
return ">>";
default:
return "?";
}
}
};
struct UnaryOperator
{
enum Kind
{
NOT,
NEG,
BIT_NOT
};
Kind kind;
UnaryOperator(Kind k) : kind(k) {}
std::string symbol() const
{
switch (kind)
{
case NOT:
return "not";
case NEG:
return "-";
case BIT_NOT:
return "~";
default:
return "?";
}
}
};

192
src/ast/statements.hpp Normal file
View file

@ -0,0 +1,192 @@
#pragma once
#include <iostream>
#include <sstream>
#include "string_utils.hpp"
#include "base.hpp"
template <typename T>
using ptr = std::unique_ptr<T>;
struct Assign : Stmt
{
ptr<Expr> target;
ptr<Expr> value;
Assign(ptr<Expr> t, ptr<Expr> v)
: target(std::move(t)), value(std::move(v)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "Assign\n";
oss << target->dump(indent + 1) << "\n";
oss << value->dump(indent + 1);
return oss.str();
}
};
struct ExprStmt : Stmt
{
ptr<Expr> expr;
ExprStmt(ptr<Expr> e) : expr(std::move(e)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "ExprStmt\n";
oss << expr->dump(indent + 1);
return oss.str();
}
};
struct Return : Stmt
{
ptr<Expr> value;
Return(ptr<Expr> v) : value(std::move(v)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "Return\n";
if (value)
{
oss << value->dump(indent + 1);
}
return oss.str();
}
};
struct Pass : Stmt
{
std::string dump(int indent = 0) const override
{
return indent_str(indent) + "Pass";
}
};
struct If : Stmt
{
ptr<Expr> condition;
std::vector<ptr<Stmt>> then_body;
std::vector<ptr<Stmt>> else_body;
If(ptr<Expr> cond, std::vector<ptr<Stmt>> then_b, std::vector<ptr<Stmt>> else_b)
: condition(std::move(cond)),
then_body(std::move(then_b)),
else_body(std::move(else_b)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "If\n";
oss << condition->dump(indent + 1);
for (const auto &stmt : then_body)
{
oss << "\n"
<< stmt->dump(indent + 1);
}
if (!else_body.empty())
{
oss << "\n"
<< indent_str(indent + 1) << "Else";
for (const auto &stmt : else_body)
{
oss << "\n"
<< stmt->dump(indent + 1);
}
}
return oss.str();
}
};
struct For : Stmt
{
std::string target;
ptr<Expr> iter;
std::vector<ptr<Stmt>> body;
For(std::string t, ptr<Expr> i, std::vector<ptr<Stmt>> b)
: target(t), iter(std::move(i)), body(std::move(b)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "For(" << target << ")\n";
oss << iter->dump(indent + 1);
for (const auto &stmt : body)
{
oss << "\n"
<< stmt->dump(indent + 1);
}
return oss.str();
}
};
struct FunctionDef : Stmt
{
std::string name;
std::vector<std::string> params;
std::vector<ptr<Stmt>> body;
FunctionDef(std::string n, std::vector<std::string> p, std::vector<ptr<Stmt>> b)
: name(n), params(std::move(p)), body(std::move(b)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "FunctionDef(" << name << ")";
for (const auto &param : params)
{
oss << "\n"
<< indent_str(indent + 1) << "Param(" << param << ")";
}
for (const auto &stmt : body)
{
oss << "\n"
<< stmt->dump(indent + 1);
}
return oss.str();
}
};
struct ClassDef : Stmt
{
std::string name;
std::vector<ptr<Stmt>> body;
ClassDef(std::string n, std::vector<ptr<Stmt>> b)
: name(n), body(std::move(b)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << indent_str(indent) << "ClassDef(" << name << ")";
for (const auto &stmt : body)
{
oss << "\n"
<< stmt->dump(indent + 1);
}
return oss.str();
}
};
struct Module : Node
{
std::vector<ptr<Stmt>> body;
Module(std::vector<ptr<Stmt>> b) : body(std::move(b)) {}
std::string dump(int indent = 0) const override
{
std::ostringstream oss;
oss << "Module";
for (const auto &stmt : body)
{
oss << "\n"
<< stmt->dump(indent + 1);
}
return oss.str();
}
};

7
src/ast/string_utils.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <string>
inline std::string indent_str(int level)
{
return std::string(level * 2, ' ');
}

View file

@ -1,5 +1,6 @@
#include "parser.hpp"
#include "scanner.hpp"
#include "ast.hpp"
#include <iostream>
#include <fstream>
@ -36,6 +37,7 @@ int main(int argc, char *argv[])
std::ifstream file_stream;
bool token_dump = false;
bool ast_dump = false;
for (int i = 1; i < argc; ++i)
{
@ -49,6 +51,10 @@ int main(int argc, char *argv[])
{
token_dump = true;
}
else if (arg == "--dump-ast")
{
ast_dump = true;
}
}
Scanner scanner(*input_stream);
@ -57,6 +63,13 @@ int main(int argc, char *argv[])
dump_tokens(scanner);
return 0;
}
yy::parser parser(&scanner);
return parser.parse();
std::unique_ptr<Module> ast_root;
yy::parser parser(&scanner, &ast_root);
parser.parse();
if (ast_dump)
{
std::cout << ast_root->dump();
}
}

View file

@ -11,6 +11,7 @@
%code requires {
#include <string>
#include "ast.hpp"
class Scanner;
}
@ -23,52 +24,60 @@
}
%parse-param { Scanner* scanner }
%parse-param { std::unique_ptr<Module>* result }
// ---------------------- Semantic tokens ----------------------
%token <std::string> IDENTIFIER STRING
%token <int> NUMBER
%token <std::string> NUMBER
// ---------------------- Keywords ----------------------
%left KW_OR
%left KW_AND
%right KW_NOT // unary logical NOT
%token KW_DEF KW_IF KW_RETURN KW_FOR KW_IN KW_PASS KW_ELSE KW_ELIF KW_CLASS
// ---------------------- Structure / punctuation ----------------------
%token L_PAREN R_PAREN L_BRACKET R_BRACKET L_BRACE R_BRACE
%token DOT COLON SEMICOLON COMMA
%token INDENT DEDENT NEWLINE EOF
// ---------------------- Operators ----------------------
// Bitwise
%left BIT_OR
%left BIT_XOR
%left BIT_AND
%left LSHIFT RSHIFT
%token BIT_NOT
// Comparison
%nonassoc EQ NE LT LTE GT GTE
// Arithmetic
%left PLUS MINUS
%left MULTIPLY DIVIDE MODULO
%right UMINUS // unary minus
// Assignment (right-associative)
%right ASSIGN PLUS_ASSIGN MINUS_ASSIGN MULTIPLY_ASSIGN DIVIDE_ASSIGN MODULO_ASSIGN
%right BIT_AND_ASSIGN BIT_OR_ASSIGN BIT_XOR_ASSIGN LSHIFT_ASSIGN RSHIFT_ASSIGN
// Add types for your grammar rules:
%type <std::unique_ptr<Expr>> expr
%type <std::unique_ptr<Stmt>> stmt
%type <std::vector<std::unique_ptr<Stmt>>> stmt_list
%%
input:
/* empty */
| input element
stmt_list EOF { *result = std::make_unique<Module>(std::move($1));}
;
element:
IDENTIFIER { std::cout << "IDENTIFIER: " << $1 << "\n"; }
stmt_list:
/* empty */ { $$ = std::vector<std::unique_ptr<Stmt>>(); }
| stmt_list stmt { $1.push_back(std::move($2)); $$ = std::move($1); }
;
stmt:
expr { $$ = std::make_unique<ExprStmt>(std::move($1)); }
;
expr:
NUMBER { $$ = std::make_unique<Number>($1); }
| IDENTIFIER { $$ = std::make_unique<Identifier>($1); }
| expr PLUS expr { $$ = std::make_unique<BinOp>(
BinaryOperator(BinaryOperator::ADD),
std::move($1), std::move($3)); }
;
%%

View file

@ -10,6 +10,8 @@
class Scanner : public yyFlexLexer
{
bool eof_emitted = false;
public:
IndentStack indent_stack;
Scanner(std::istream &in) : yyFlexLexer(&in), yylval(nullptr) {}
@ -23,6 +25,8 @@ public:
this->yylval = lval;
int token = yylex();
// std::cout << "lex: " << yy::parser::symbol_name(static_cast<yy::parser::symbol_kind_type>(token)) << '\n';
if (token == yy::parser::token::TOK_EOF)
{
auto result = indent_stack.closeBlock();
@ -33,6 +37,17 @@ public:
}
}
if (token != yy::parser::token::TOK_EOF)
{
return token;
}
if (eof_emitted)
{
return 0;
}
eof_emitted = true;
return token;
}