mirror of
https://github.com/lisk77/lambda.git
synced 2025-10-24 18:28: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 "parser.hpp"
|
||||||
#include "ast.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 {
|
const Token& Parser::peek() const {
|
||||||
if (position >= tokens.size())
|
if (position >= tokens.size())
|
||||||
throw std::runtime_error("parser: unexpected end of file");
|
throw std::runtime_error("parser: unexpected end of file");
|
||||||
return tokens[position];
|
return tokens[position];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token& Parser::get() {
|
const Token& Parser::get() {
|
||||||
if (position >= tokens.size())
|
if (position >= tokens.size())
|
||||||
throw std::runtime_error("parser: unexpected end of file");
|
throw std::runtime_error("parser: unexpected end of file");
|
||||||
return tokens[position++];
|
return tokens[position++];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::accept(TokenType t) {
|
bool Parser::accept(TokenType t) {
|
||||||
if (position < tokens.size() && tokens[position].type == t) {
|
if (position < tokens.size() && tokens[position].type == t) {
|
||||||
++position;
|
++position;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
params.push_back(get().lexeme);
|
||||||
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();
|
if (peek().type == TokenType::VARIABLE) {
|
||||||
// right-nest them: \p1.\p2.…body
|
const auto &tok = get();
|
||||||
for (auto it = params.rbegin(); it != params.rend(); ++it) {
|
return std::make_unique<Variable>(tok.lexeme);
|
||||||
body = std::make_unique<Abstraction>(*it, std::move(body));
|
|
||||||
}
|
}
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
// variable
|
if (accept(TokenType::LPAREN)) {
|
||||||
if (peek().type == TokenType::VARIABLE) {
|
std::unique_ptr<Expr> e = parseApplication();
|
||||||
const auto &tok = get();
|
expect(TokenType::RPAREN);
|
||||||
return std::make_unique<Variable>(tok.lexeme);
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parenthesized term
|
throw std::runtime_error("parser: expected \\, variable, or '(' at position " + std::to_string(peek().start));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// <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 ||
|
std::unique_ptr<Expr> rhs = parseSimple();
|
||||||
t == TokenType::LPAREN)
|
expr = std::make_unique<Application>(std::move(expr), std::move(rhs));
|
||||||
{
|
}
|
||||||
std::unique_ptr<Expr> rhs = parseSimple();
|
else break;
|
||||||
expr = std::make_unique<Application>(std::move(expr), std::move(rhs));
|
|
||||||
} 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// thank you chatgpt lol
|
||||||
|
|
||||||
// Forward‐declare
|
// Forward‐declare
|
||||||
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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue