mirror of
https://github.com/lisk77/lambda.git
synced 2025-10-24 02:08:49 +00:00
docs: added parser documentation
This commit is contained in:
parent
bce708be5f
commit
777ff3d3fb
2 changed files with 69 additions and 104 deletions
169
src/parser.cpp
169
src/parser.cpp
|
|
@ -1,4 +1,3 @@
|
|||
// src/parser.cpp
|
||||
#include "parser.hpp"
|
||||
#include "ast.hpp"
|
||||
|
||||
|
|
@ -20,139 +19,103 @@ const std::unordered_map<std::string, std::unique_ptr<Expr>>& Parser::definition
|
|||
}
|
||||
|
||||
const Token& Parser::peek() const {
|
||||
if (position >= tokens.size())
|
||||
throw std::runtime_error("parser: unexpected end of file");
|
||||
return tokens[position];
|
||||
if (position >= tokens.size())
|
||||
throw std::runtime_error("parser: unexpected end of file");
|
||||
return tokens[position];
|
||||
}
|
||||
|
||||
const Token& Parser::get() {
|
||||
if (position >= tokens.size())
|
||||
throw std::runtime_error("parser: unexpected end of file");
|
||||
return tokens[position++];
|
||||
if (position >= tokens.size())
|
||||
throw std::runtime_error("parser: unexpected end of file");
|
||||
return tokens[position++];
|
||||
}
|
||||
|
||||
bool Parser::accept(TokenType t) {
|
||||
if (position < tokens.size() && tokens[position].type == t) {
|
||||
++position;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (position < tokens.size() && tokens[position].type == t) {
|
||||
++position;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parser::expect(TokenType t) {
|
||||
if (!accept(t)) {
|
||||
std::cout << display_tokentype(t) << std::endl;
|
||||
throw std::runtime_error("parser: unexpected token at position "
|
||||
+ std::to_string(peek().start));
|
||||
}
|
||||
if (!accept(t)) {
|
||||
std::cout << display_tokentype(t) << std::endl;
|
||||
throw std::runtime_error("parser: unexpected token at position " + std::to_string(peek().start));
|
||||
}
|
||||
}
|
||||
|
||||
// Try to parse a definition of the form:
|
||||
// <variable> '=' <term> ';'
|
||||
// Returns true if a definition was parsed and stored in defs.
|
||||
// parse expressions like "main = \ x . x;"
|
||||
bool Parser::tryParseDefinition() {
|
||||
if (peek().type == TokenType::VARIABLE
|
||||
&& position+1 < tokens.size()
|
||||
&& tokens[position+1].type == TokenType::EQUALS)
|
||||
{
|
||||
std::string name = get().lexeme; // consume VARIABLE
|
||||
expect(TokenType::EQUALS); // consume '='
|
||||
if (peek().type == TokenType::VARIABLE && position+1 < tokens.size() && tokens[position+1].type == TokenType::EQUALS) {
|
||||
std::string name = get().lexeme;
|
||||
expect(TokenType::EQUALS);
|
||||
|
||||
// parse the right-hand term
|
||||
std::unique_ptr<Expr> value = parseTerm();
|
||||
std::unique_ptr<Expr> value = parseApplication();
|
||||
|
||||
// require a semicolon (EOL token)
|
||||
expect(TokenType::EOL);
|
||||
expect(TokenType::EOL);
|
||||
|
||||
// store in definitions map
|
||||
defs.emplace(std::move(name), std::move(value));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
defs.emplace(std::move(name), std::move(value));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// <simple> ::= '\' <variable>+ '.' <term>
|
||||
// | <variable>
|
||||
// | '(' <term> ')'
|
||||
// parse simple expressions like "\ x . x" or "x" or stuff in parenthesis
|
||||
std::unique_ptr<Expr> Parser::parseSimple() {
|
||||
// abstraction with one-or-more parameters
|
||||
if (accept(TokenType::LAMBDA)) {
|
||||
std::vector<std::string> params;
|
||||
while (peek().type == TokenType::VARIABLE) {
|
||||
params.push_back(get().lexeme);
|
||||
if (accept(TokenType::LAMBDA)) {
|
||||
std::vector<std::string> params;
|
||||
while (peek().type == TokenType::VARIABLE) {
|
||||
params.push_back(get().lexeme);
|
||||
}
|
||||
expect(TokenType::DOT);
|
||||
|
||||
std::unique_ptr<Expr> body = parseApplication();
|
||||
for (auto it = params.rbegin(); it != params.rend(); ++it) {
|
||||
body = std::make_unique<Abstraction>(*it, std::move(body));
|
||||
}
|
||||
return body;
|
||||
}
|
||||
expect(TokenType::DOT);
|
||||
|
||||
std::unique_ptr<Expr> body = parseTerm();
|
||||
// right-nest them: \p1.\p2.…body
|
||||
for (auto it = params.rbegin(); it != params.rend(); ++it) {
|
||||
body = std::make_unique<Abstraction>(*it, std::move(body));
|
||||
if (peek().type == TokenType::VARIABLE) {
|
||||
const auto &tok = get();
|
||||
return std::make_unique<Variable>(tok.lexeme);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
// variable
|
||||
if (peek().type == TokenType::VARIABLE) {
|
||||
const auto &tok = get();
|
||||
return std::make_unique<Variable>(tok.lexeme);
|
||||
}
|
||||
if (accept(TokenType::LPAREN)) {
|
||||
std::unique_ptr<Expr> e = parseApplication();
|
||||
expect(TokenType::RPAREN);
|
||||
return e;
|
||||
}
|
||||
|
||||
// parenthesized term
|
||||
if (accept(TokenType::LPAREN)) {
|
||||
std::unique_ptr<Expr> e = parseTerm();
|
||||
expect(TokenType::RPAREN);
|
||||
return e;
|
||||
}
|
||||
|
||||
throw std::runtime_error("parser: expected \\, variable, or '(' at position "
|
||||
+ std::to_string(peek().start));
|
||||
throw std::runtime_error("parser: expected \\, variable, or '(' at position " + std::to_string(peek().start));
|
||||
}
|
||||
|
||||
// <application> ::= <simple> { <simple> }
|
||||
// parse function applications
|
||||
std::unique_ptr<Expr> Parser::parseApplication() {
|
||||
std::unique_ptr<Expr> expr = parseSimple();
|
||||
while (true) {
|
||||
TokenType t = peek().type;
|
||||
if (t == TokenType::VARIABLE ||
|
||||
t == TokenType::LAMBDA ||
|
||||
t == TokenType::LPAREN)
|
||||
{
|
||||
std::unique_ptr<Expr> rhs = parseSimple();
|
||||
expr = std::make_unique<Application>(std::move(expr), std::move(rhs));
|
||||
} else {
|
||||
break;
|
||||
std::unique_ptr<Expr> expr = parseSimple();
|
||||
while (true) {
|
||||
TokenType t = peek().type;
|
||||
if (t == TokenType::VARIABLE || t == TokenType::LAMBDA || t == TokenType::LPAREN) {
|
||||
std::unique_ptr<Expr> rhs = parseSimple();
|
||||
expr = std::make_unique<Application>(std::move(expr), std::move(rhs));
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
return expr;
|
||||
}
|
||||
|
||||
// <term> ::= <application>
|
||||
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
|
||||
// uuum... i think the name says it?
|
||||
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) {
|
||||
// first try a definition
|
||||
if (tryParseDefinition())
|
||||
continue;
|
||||
while (peek().type != TokenType::EOC) {
|
||||
if (tryParseDefinition()) continue;
|
||||
|
||||
// otherwise a bare term ending in EOL
|
||||
results.push_back(parseTerm());
|
||||
expect(TokenType::EOL);
|
||||
}
|
||||
results.push_back(parseApplication());
|
||||
expect(TokenType::EOL);
|
||||
}
|
||||
|
||||
expect(TokenType::EOC);
|
||||
return results;
|
||||
expect(TokenType::EOC);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
// thank you chatgpt lol
|
||||
|
||||
// Forward‐declare
|
||||
static void _print(const Expr* e, std::ostream& out);
|
||||
|
||||
|
|
@ -51,6 +53,6 @@ static void _print(const Expr* e, std::ostream& out) {
|
|||
}
|
||||
|
||||
// fallback
|
||||
out << "<??>";
|
||||
out << "print: unknown expression";
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue