docs: added parser documentation

This commit is contained in:
lisk77 2025-06-30 21:07:29 +02:00
parent bce708be5f
commit 777ff3d3fb
2 changed files with 69 additions and 104 deletions

View file

@ -1,4 +1,3 @@
// src/parser.cpp
#include "parser.hpp" #include "parser.hpp"
#include "ast.hpp" #include "ast.hpp"
@ -42,40 +41,28 @@ bool Parser::accept(TokenType t) {
void Parser::expect(TokenType t) { void Parser::expect(TokenType t) {
if (!accept(t)) { if (!accept(t)) {
std::cout << display_tokentype(t) << std::endl; std::cout << display_tokentype(t) << std::endl;
throw std::runtime_error("parser: unexpected token at position " throw std::runtime_error("parser: unexpected token at position " + std::to_string(peek().start));
+ std::to_string(peek().start));
} }
} }
// Try to parse a definition of the form: // parse expressions like "main = \ x . x;"
// <variable> '=' <term> ';'
// Returns true if a definition was parsed and stored in defs.
bool Parser::tryParseDefinition() { bool Parser::tryParseDefinition() {
if (peek().type == TokenType::VARIABLE if (peek().type == TokenType::VARIABLE && position+1 < tokens.size() && tokens[position+1].type == TokenType::EQUALS) {
&& position+1 < tokens.size() std::string name = get().lexeme;
&& tokens[position+1].type == TokenType::EQUALS) expect(TokenType::EQUALS);
{
std::string name = get().lexeme; // consume VARIABLE
expect(TokenType::EQUALS); // consume '='
// parse the right-hand term std::unique_ptr<Expr> value = parseApplication();
std::unique_ptr<Expr> value = parseTerm();
// require a semicolon (EOL token)
expect(TokenType::EOL); expect(TokenType::EOL);
// store in definitions map
defs.emplace(std::move(name), std::move(value)); defs.emplace(std::move(name), std::move(value));
return true; return true;
} }
return false; return false;
} }
// <simple> ::= '\' <variable>+ '.' <term> // parse simple expressions like "\ x . x" or "x" or stuff in parenthesis
// | <variable>
// | '(' <term> ')'
std::unique_ptr<Expr> Parser::parseSimple() { std::unique_ptr<Expr> Parser::parseSimple() {
// abstraction with one-or-more parameters
if (accept(TokenType::LAMBDA)) { if (accept(TokenType::LAMBDA)) {
std::vector<std::string> params; std::vector<std::string> params;
while (peek().type == TokenType::VARIABLE) { while (peek().type == TokenType::VARIABLE) {
@ -83,76 +70,52 @@ std::unique_ptr<Expr> Parser::parseSimple() {
} }
expect(TokenType::DOT); expect(TokenType::DOT);
std::unique_ptr<Expr> body = parseTerm(); std::unique_ptr<Expr> body = parseApplication();
// right-nest them: \p1.\p2.…body
for (auto it = params.rbegin(); it != params.rend(); ++it) { for (auto it = params.rbegin(); it != params.rend(); ++it) {
body = std::make_unique<Abstraction>(*it, std::move(body)); body = std::make_unique<Abstraction>(*it, std::move(body));
} }
return body; return body;
} }
// variable
if (peek().type == TokenType::VARIABLE) { if (peek().type == TokenType::VARIABLE) {
const auto &tok = get(); const auto &tok = get();
return std::make_unique<Variable>(tok.lexeme); return std::make_unique<Variable>(tok.lexeme);
} }
// parenthesized term
if (accept(TokenType::LPAREN)) { if (accept(TokenType::LPAREN)) {
std::unique_ptr<Expr> e = parseTerm(); std::unique_ptr<Expr> e = parseApplication();
expect(TokenType::RPAREN); expect(TokenType::RPAREN);
return e; return e;
} }
throw std::runtime_error("parser: expected \\, variable, or '(' at position " throw std::runtime_error("parser: expected \\, variable, or '(' at position " + std::to_string(peek().start));
+ std::to_string(peek().start));
} }
// <application> ::= <simple> { <simple> } // parse function applications
std::unique_ptr<Expr> Parser::parseApplication() { std::unique_ptr<Expr> Parser::parseApplication() {
std::unique_ptr<Expr> expr = parseSimple(); std::unique_ptr<Expr> expr = parseSimple();
while (true) { while (true) {
TokenType t = peek().type; TokenType t = peek().type;
if (t == TokenType::VARIABLE || if (t == TokenType::VARIABLE || t == TokenType::LAMBDA || t == TokenType::LPAREN) {
t == TokenType::LAMBDA ||
t == TokenType::LPAREN)
{
std::unique_ptr<Expr> rhs = parseSimple(); std::unique_ptr<Expr> rhs = parseSimple();
expr = std::make_unique<Application>(std::move(expr), std::move(rhs)); expr = std::make_unique<Application>(std::move(expr), std::move(rhs));
} else {
break;
} }
else break;
} }
return expr; return expr;
} }
// <term> ::= <application> // uuum... i think the name says it?
std::unique_ptr<Expr> Parser::parseTerm() {
return parseApplication();
}
// parse exactly one term and expect EOC
std::unique_ptr<Expr> Parser::parse() {
std::unique_ptr<Expr> root = parseTerm();
expect(TokenType::EOC);
return root;
}
// <program> ::= { <definition> | <term> EOL } EOC
std::vector<std::unique_ptr<Expr>> Parser::parseProgram() { std::vector<std::unique_ptr<Expr>> Parser::parseProgram() {
std::vector<std::unique_ptr<Expr>> results; std::vector<std::unique_ptr<Expr>> results;
while (peek().type != TokenType::EOC) { while (peek().type != TokenType::EOC) {
// first try a definition if (tryParseDefinition()) continue;
if (tryParseDefinition())
continue;
// otherwise a bare term ending in EOL results.push_back(parseApplication());
results.push_back(parseTerm());
expect(TokenType::EOL); expect(TokenType::EOL);
} }
expect(TokenType::EOC); expect(TokenType::EOC);
return results; return results;
} }

View file

@ -3,6 +3,8 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
// thank you chatgpt lol
// Forwarddeclare // Forwarddeclare
static void _print(const Expr* e, std::ostream& out); static void _print(const Expr* e, std::ostream& out);
@ -51,6 +53,6 @@ static void _print(const Expr* e, std::ostream& out) {
} }
// fallback // fallback
out << "<??>"; out << "print: unknown expression";
} }