199 lines
5.6 KiB
C++
199 lines
5.6 KiB
C++
#pragma once
|
|
#include <cassert>
|
|
#include "parser/lexer.hpp"
|
|
#include "parser/nodes.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('(');
|
|
Node* arg = nullptr;
|
|
// TODO: support multiple arguments
|
|
if (m_lexer->seek_token()->token != ')')
|
|
{
|
|
arg = ParseExpression();
|
|
}
|
|
m_lexer->NextExpect(')');
|
|
return new FnCallNode(name, arg);
|
|
}
|
|
|
|
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);
|
|
Exit(1);
|
|
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((int)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((int)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();
|
|
// TODO: proper error handling
|
|
assert(token != nullptr && "next token should be available");
|
|
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::Extern: program->PushExtern(ParseExtern()); break;
|
|
case TokenType::Fn: program->PushFunction(ParseFnDecl()); break;
|
|
default: {
|
|
fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token.line_number, token.offset_start, token.token);
|
|
Exit(1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return program;
|
|
}
|
|
private:
|
|
void Exit(int status)
|
|
{
|
|
std::exit(status);
|
|
}
|
|
private:
|
|
Lexer* m_lexer;
|
|
};
|