diff --git a/examples/math.funk b/examples/math.funk index f18cbf8bdf043618ab95486c96ec9f58dabd7805..c2e5a326ef3618af56a1c7a1903c6e1c90422757 100644 --- a/examples/math.funk +++ b/examples/math.funk @@ -1 +1,3 @@ -(1 + 1) / 2 * 100; +1+1; +2+2; +3+3; diff --git a/include/ast/ProgramNode.h b/include/ast/ProgramNode.h new file mode 100644 index 0000000000000000000000000000000000000000..9bc7507908a67a5ebf3ec99508155fb146eb21ee --- /dev/null +++ b/include/ast/ProgramNode.h @@ -0,0 +1,20 @@ +#pragma once +#include "ast/Node.h" + +namespace funk +{ +class ProgramNode : public Node +{ +public: + ProgramNode(const SourceLocation& loc); + ~ProgramNode(); + + void add(Node* statement); + + Node* evaluate() const override; + String to_s() const override; + +private: + Vector<Node*> statements; +}; +} // namespace funk diff --git a/include/parser/Parser.h b/include/parser/Parser.h index 4a5cda67c1bb3842d0da47f863a71bcc288064b4..008617eba55ebc92012bc349cfa9776497c4d279 100644 --- a/include/parser/Parser.h +++ b/include/parser/Parser.h @@ -13,6 +13,7 @@ #include "ast/BinaryOpNode.h" #include "ast/LiteralNode.h" #include "ast/Node.h" +#include "ast/ProgramNode.h" #include "ast/UnaryOpNode.h" namespace funk diff --git a/source/ast/ProgramNode.cc b/source/ast/ProgramNode.cc new file mode 100644 index 0000000000000000000000000000000000000000..5389f9483123eb35812bde2b88cd89721f998996 --- /dev/null +++ b/source/ast/ProgramNode.cc @@ -0,0 +1,38 @@ +#include "ast/ProgramNode.h" + +namespace funk +{ +ProgramNode::ProgramNode(const SourceLocation& loc) : Node(loc), statements{} {} + +ProgramNode::~ProgramNode() +{ + for (Node* statement : statements) { delete statement; } +} + +void ProgramNode::add(Node* statement) +{ + statements.push_back(statement); +} + +Node* ProgramNode::evaluate() const +{ + if (cached_eval) { return cached_eval; } + + Node* result{}; + for (Node* statement : statements) { result = statement->evaluate(); } + return cached_eval = result; +} + +String ProgramNode::to_s() const +{ + String repr{}; + + for (Node* statement : statements) + { + repr += statement->to_s(); + repr += '\n'; + } + + return repr; +} +} // namespace funk diff --git a/source/lexer/Lexer.cc b/source/lexer/Lexer.cc index 2e0c112325f3fbf0a9f548c0fae9f88ad43ecc00..a4ad4f48a810c71ef98585f047f9eaa87c550d2f 100644 --- a/source/lexer/Lexer.cc +++ b/source/lexer/Lexer.cc @@ -37,13 +37,12 @@ Vector<Token> Lexer::tokenize() Token Lexer::next_token() { skip_whitespace(); + token_start_col = column; if (done()) { return make_token("", TokenType::EOF_TOKEN); } char c{peek()}; - token_start_col = column; - if (is_digit(c)) { return get_number(); } else if (is_alpha(c)) { return get_identifier(); } else if (c == '"') { return get_text(); } @@ -174,8 +173,8 @@ void Lexer::skip_whitespace() case '\t': next(); break; case '\n': line++; - column = 1; next(); + column = 1; break; default: return; } diff --git a/source/main.cc b/source/main.cc index ab7916d403445d22e31e37bf646296f64c4e0def..5fba43d16accbbe298b04216de9f4a0c7ade220a 100644 --- a/source/main.cc +++ b/source/main.cc @@ -111,7 +111,13 @@ void process_file(const String& file, const Config& config) Node* ast = parser.parse(); LOG_DEBUG("File parsed!"); - if (config.ast) { LOG_INFO("AST: " + ast->to_s()); } + if (config.ast) + { + LOG_INFO("Abstract Syntax Tree:"); + std::istringstream stream(ast->to_s()); + String line; + while (std::getline(stream, line)) { LOG_INFO(line); } + } LOG_DEBUG("Evaluating AST..."); Node* res{ast->evaluate()}; diff --git a/source/parser/Parser.cc b/source/parser/Parser.cc index ae664d7382f19a7a2ad1fba5397848b63e9ccf63..949eaf2e90ef4ca1a04375c05700ebed4955a81b 100644 --- a/source/parser/Parser.cc +++ b/source/parser/Parser.cc @@ -10,7 +10,10 @@ Parser::~Parser() {} Node* Parser::parse() { LOG_DEBUG("Parse program"); - return parse_statement(); + + ProgramNode* program = new ProgramNode(SourceLocation(filename, 0, 0)); + while (!done()) { program->add(parse_statement()); } + return program; } Parser Parser::load(String filename) @@ -42,7 +45,7 @@ Token Parser::peek_next() const bool Parser::done() const { - return index >= static_cast<int>(tokens.size()); + return peek().get_type() == TokenType::EOF_TOKEN; } bool Parser::check(TokenType type) const @@ -61,7 +64,15 @@ Node* Parser::parse_statement() { LOG_DEBUG("Parse statement"); Node* expr{parse_expression()}; - if (!match(TokenType::SEMICOLON)) throw SyntaxError(peek_prev().get_location(), "Expected ';'"); + + if (!match(TokenType::SEMICOLON)) + { + Token prev{peek_prev()}; + SourceLocation loc{prev.get_location()}; + SourceLocation error_loc(loc.filename, loc.line, static_cast<int>(loc.column + prev.get_lexeme().length())); + throw SyntaxError(error_loc, "Expected ';'"); + } + return expr; } @@ -185,8 +196,7 @@ Node* Parser::parse_unary() Node* Parser::parse_factor() { LOG_DEBUG("Parse factor"); - if (check(TokenType::IDENTIFIER)) - return parse_identifier(); + if (check(TokenType::IDENTIFIER)) { return parse_identifier(); } else if (check(TokenType::NUMB) || check(TokenType::REAL) || check(TokenType::BOOL) || check(TokenType::CHAR) || check(TokenType::TEXT)) { @@ -195,11 +205,19 @@ Node* Parser::parse_factor() else if (match(TokenType::L_PAR)) { Node* expr{parse_expression()}; - if (!match(TokenType::R_PAR)) throw SyntaxError(peek_prev().get_location(), "Expected ')'"); + + if (!match(TokenType::R_PAR)) + { + Token prev{peek_prev()}; + SourceLocation loc{prev.get_location()}; + SourceLocation error_loc(loc.filename, loc.line, static_cast<int>(loc.column + prev.get_lexeme().length())); + throw SyntaxError(error_loc, "Expected ')'"); + } + return expr; } - throw SyntaxError(peek().get_location(), "???"); + throw SyntaxError(peek_prev().get_location(), "Expected expression, got " + token_type_to_s(peek().get_type())); } Node* Parser::parse_literal()