Files
pl/include/parser/ast.hpp
2026-01-06 16:39:51 +01:00

200 lines
5.9 KiB
C++

#pragma once
#include <cassert>
#include "parser/lexer.hpp"
#include "parser/nodes.hpp"
#include "prelude/error.hpp"
class AstParser
{
public:
AstParser(Lexer* lexer)
: m_lexer(lexer) {}
public:
ExternNode* ParseExtern()
{
m_lexer->NextExpect(TokenType::Id);
return new ExternNode(m_lexer->token().string);
}
FnDeclNode* ParseFnDecl()
{
// Function Declaration
m_lexer->NextExpect(TokenType::Id);
StringView name = m_lexer->token().string;
m_lexer->NextExpect('(');
Builder<StringView> params;
// TODO: support multiple params
if (m_lexer->seek_token()->token != ')')
{
m_lexer->NextExpect(TokenType::Id);
params.Push(m_lexer->token().string);
}
m_lexer->NextExpect(')');
m_lexer->NextExpect('{');
auto compound = new CompoundNode();
while (m_lexer->seek_token()->token != '}')
{
compound->addNode(ParseStatement());
}
m_lexer->NextExpect('}');
return new FnDeclNode(name, compound, params.view());
}
FnCallNode* ParseFnCall(const StringView& name)
{
// m_lexer->NextExpect(TokenType::Id);
// char* name = strdup(m_lexer->token().string);
m_lexer->NextExpect('(');
Builder<Node*> args;
while (m_lexer->seek_token()->token != ')')
{
auto arg = ParseExpression();
args.Push(arg);
if (m_lexer->seek_token()->token == ',') assert(m_lexer->NextToken());
}
m_lexer->NextExpect(')');
return new FnCallNode(name, std::move(args.view()));
}
Node* ParseFactor()
{
auto token = m_lexer->seek_token();
switch (token->token)
{
case TokenType::IntLiteral: // integer
{
m_lexer->NextExpect(TokenType::IntLiteral);
auto node = new IntLiteralNode(m_lexer->token().int_number);
return node;
}
case TokenType::Id: // variable name or function call
{
m_lexer->NextExpect(TokenType::Id);
auto name = m_lexer->token().string;
token = m_lexer->seek_token();
if (token->token == '(')
{
return ParseFnCall(name);
}
return new VariableNode(name);
}
default:
// fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token->line_number, token->offset_start, token->token);
ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%d'", token->token), token->line_number, token->offset_start));
break;
}
assert(0 && "unreachable");
}
Node* ParseTerm()
{
auto t = ParseFactor();
for (auto op = m_lexer->seek_token(); is_one_of(op->token, '/', '*'); op = m_lexer->seek_token())
{
m_lexer->NextToken();
ExpressionNode::Operator eop;
assert(static_cast<size_t>(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled");
switch((char)op->token)
{
case '/':
eop = ExpressionNode::Operator::Divide;
break;
case '*':
eop = ExpressionNode::Operator::Multiply;
break;
default:
assert(false && "should be unreachable");
break;
}
auto expr = new ExpressionNode(t, ParseTerm(), eop);
t = expr;
}
return t;
}
Node* ParseExpression()
{
auto t = ParseTerm();
for (auto op = m_lexer->seek_token(); is_one_of(op->token, '+', '-'); op = m_lexer->seek_token())
{
m_lexer->NextToken();
ExpressionNode::Operator eop;
assert(static_cast<size_t>(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled");
switch((char)op->token)
{
case '+':
eop = ExpressionNode::Operator::Plus;
break;
case '-':
eop = ExpressionNode::Operator::Minus;
break;
default:
assert(false && "should be unreachable");
break;
}
auto expr = new ExpressionNode(t, ParseTerm(), eop);
t = expr;
}
return t;
}
VarDeclNode* ParseVarDecl()
{
m_lexer->NextExpect(TokenType::Local);
m_lexer->NextExpect(TokenType::Id);
auto name = m_lexer->token().string;
m_lexer->NextExpect('=');
Node* value = ParseExpression();
return new VarDeclNode(name, value);
}
Node* ParseStatement()
{
auto token = m_lexer->seek_token();
assert(token != nullptr);
switch(token->token)
{
case TokenType::Local: return ParseVarDecl();
default: return ParseExpression();
}
assert(0 && "unreachable");
return nullptr;
}
ProgramNode* Parse()
{
auto program = new ProgramNode;
while (m_lexer->NextToken())
{
auto token = m_lexer->token();
switch(token.token)
{
case TokenType::Fn: program->PushFunction(ParseFnDecl()); break;
case TokenType::Extern: program->PushExtern(ParseExtern()); break;
default: {
ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%d'", token.token), token.line_number, token.offset_start));
assert(false);
}
break;
}
}
return program;
}
private:
void Exit(int status)
{
std::exit(status);
}
private:
Lexer* m_lexer;
};