From 1c1d754750996a5f21fd5dd5f2a04a9f5798253c Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sat, 28 Jun 2025 22:51:02 +0100 Subject: [PATCH] Finish parsing expressions --- cclox_src/error_handler.cc | 9 ++ cclox_src/error_handler.hh | 2 + cclox_src/interpreter.cc | 10 +- cclox_src/main.cc | 24 +---- cclox_src/parser.cc | 1 + cclox_src/parser.hh | 5 +- cclox_src/parser/parser.cc | 186 ++++++++++++++++++++++++++++++++++++ cclox_src/parser/parser.hh | 47 +++++++++ cclox_src/parser/printer.cc | 4 + cclox_src/scanner/object.cc | 5 + 10 files changed, 270 insertions(+), 23 deletions(-) create mode 100644 cclox_src/parser/parser.cc create mode 100644 cclox_src/parser/parser.hh diff --git a/cclox_src/error_handler.cc b/cclox_src/error_handler.cc index 5f2b0dd..a8ddc73 100644 --- a/cclox_src/error_handler.cc +++ b/cclox_src/error_handler.cc @@ -1,10 +1,19 @@ #include "error_handler.hh" +#include "tokenizer.hh" #include void ErrorHandler::error(int line, const std::string &message) { report(line, "", message); } +void ErrorHandler::error(Token token, const std::string &message) { + if (token.type == TokenType::EOFILE) { + report(token.line, " at end", message); + } else { + report(token.line, " at '" + token.lexeme + "'", message); + } +} + void ErrorHandler::report(int line, const std::string &where, const std::string &message) { std::cout << "[line " << line << "] Error" << where << ": " << message << '\n'; had_error = true; diff --git a/cclox_src/error_handler.hh b/cclox_src/error_handler.hh index 5d22a85..971afe3 100644 --- a/cclox_src/error_handler.hh +++ b/cclox_src/error_handler.hh @@ -1,11 +1,13 @@ #ifndef ERROR_HANDLER_HH #define ERROR_HANDLER_HH +#include "tokenizer.hh" #include struct ErrorHandler { ErrorHandler() : had_error{false} {}; void error(int line, const std::string &message); + void error(Token token, const std::string &message); void report(int line, const std::string &where, const std::string &message); bool had_error; diff --git a/cclox_src/interpreter.cc b/cclox_src/interpreter.cc index a495c02..de932c7 100644 --- a/cclox_src/interpreter.cc +++ b/cclox_src/interpreter.cc @@ -1,9 +1,11 @@ #include "interpreter.hh" #include "error_handler.hh" #include "tokenizer.hh" +#include "parser.hh" #include #include #include +#include #include #define PROMPT "> " @@ -50,7 +52,11 @@ void run(const std::string &code) { Scanner scanner{code}; std::vector tokens = scanner.scan_tokens(); - for (const auto &token : tokens) { - std::cout << token << '\n'; + Parser parser{tokens}; + std::optional expression = parser.parse(); + + if (expression) { + AstPrinter printer{}; + std::cout << printer.print(*expression) << '\n'; } } diff --git a/cclox_src/main.cc b/cclox_src/main.cc index 58fd9ce..4492536 100644 --- a/cclox_src/main.cc +++ b/cclox_src/main.cc @@ -8,26 +8,12 @@ ErrorHandler error_handler{}; int main(int argc, char *argv[]) { - (void)argc; - (void)argv; - // if (argc > 2) { - // std::cout << "Usage: cclox [script]\n"; - // exit(EX_USAGE); - // } - // - // run_interpreter(argc, argv); + if (argc > 2) { + std::cout << "Usage: cclox [script]\n"; + exit(EX_USAGE); + } - Token unary_op{TokenType::MINUS, "-", Object{}, 1}; - Expr left{Unary(unary_op, Literal(123))}; - - Token op{TokenType::STAR, "*", Object{}, 1}; - - Expr right{Grouping(Literal(45.67))}; - - Expr expr{Binary(left, op, right)}; - - AstPrinter printer{}; - std::cout << printer.print(expr) << '\n'; + run_interpreter(argc, argv); return 0; } diff --git a/cclox_src/parser.cc b/cclox_src/parser.cc index 17db8d5..22ea4db 100644 --- a/cclox_src/parser.cc +++ b/cclox_src/parser.cc @@ -3,5 +3,6 @@ #include "parser/expr.cc" #include "parser/printer.cc" +#include "parser/parser.cc" #endif diff --git a/cclox_src/parser.hh b/cclox_src/parser.hh index 167b802..ca0fc5d 100644 --- a/cclox_src/parser.hh +++ b/cclox_src/parser.hh @@ -1,7 +1,8 @@ -#ifndef PARSER_HH -#define PARSER_HH +#ifndef MAIN_PARSER_HH +#define MAIN_PARSER_HH #include "parser/expr.hh" #include "parser/printer.hh" +#include "parser/parser.hh" #endif diff --git a/cclox_src/parser/parser.cc b/cclox_src/parser/parser.cc new file mode 100644 index 0000000..7bbd85a --- /dev/null +++ b/cclox_src/parser/parser.cc @@ -0,0 +1,186 @@ +#include "parser.hh" +#include "expr.hh" +#include "../error_handler.hh" +#include "../tokenizer.hh" +#include + +extern ErrorHandler error_handler; + +std::optional Parser::parse() { + try { + return expression(); + } catch (ParseException e) { + return std::nullopt; + } +} + +Expr Parser::expression() { + return equality(); +} + +Expr Parser::equality() { + Expr expr = comparison(); + + while (match({TokenType::BANG_EQUAL, TokenType::EQUAL_EQUAL})) { + Token op = previous(); + Expr right = comparison(); + expr = Binary(expr, op, right); + } + + return expr; +} + +Expr Parser::comparison() { + Expr expr = term(); + + while (match({TokenType::GREATER, TokenType::GREATER_EQUAL, TokenType::LESS, TokenType::LESS_EQUAL})) { + Token op = previous(); + Expr right = term(); + expr = Binary(expr, op, right); + } + + return expr; +} + +Expr Parser::term() { + Expr expr = factor(); + + while (match({TokenType::MINUS, TokenType::PLUS})) { + Token op = previous(); + Expr right = factor(); + expr = Binary(expr, op, right); + } + + return expr; +} + +Expr Parser::factor() { + Expr expr = unary(); + + while (match({TokenType::SLASH, TokenType::STAR})) { + Token op = previous(); + Expr right = unary(); + expr = Binary(expr, op, right); + } + + return expr; +} + +Expr Parser::unary() { + if (match({TokenType::BANG, TokenType::MINUS})) { + Token op = previous(); + Expr right = unary(); + + return Unary(op, right); + } + + return primary(); +} + +Expr Parser::primary() { + if (match({TokenType::FALSE})) { + return Literal(false); + } + + if (match({TokenType::TRUE})) { + return Literal(true); + } + + if (match({TokenType::NIL})) { + return Literal(Object{}); + } + + if (match({TokenType::NUMBER, TokenType::STRING})) { + return Literal(previous().literal); + } + + if (match({TokenType::LEFT_PAREN})) { + Expr expr = expression(); + consume(TokenType::RIGHT_PAREN, "Expect ')' after expression."); + return Grouping(expr); + } + + throw error(peek(), "Expect expression."); +} + +Token Parser::advance() { + if (!is_at_end()) { + ++current; + } + + return previous(); +} + +Token Parser::peek() { + return tokens.at(current); +} + +Token Parser::previous() { + return tokens.at(current - 1); +} + +Token Parser::consume(TokenType type, std::string &&message) { + if (check(type)) { + return advance(); + } + + throw error(peek(), message); +} + +bool Parser::match(std::vector types) { + for (const auto &type : types) { + if (check(type)) { + advance(); + return true; + } + } + + return false; +} + +bool Parser::check(TokenType type) { + if (is_at_end()) { + return false; + } + + return peek().type == type; +} + +bool Parser::is_at_end() { + return peek().type == TokenType::EOFILE; +} + +ParseException Parser::error(Token token, std::string &message) { + error_handler.error(token, message); + return ParseException{}; +} + +ParseException Parser::error(Token token, std::string &&message) { + return error(token, message); +} + +void Parser::synchronize() { + advance(); + + while (!is_at_end()) { + if (previous().type == TokenType::SEMICOLON) { + return; + } + + switch (peek().type) { + case TokenType::CLASS: + case TokenType::FUN: + case TokenType::VAR: + case TokenType::FOR: + case TokenType::IF: + case TokenType::WHILE: + case TokenType::PRINT: + case TokenType::RETURN: + return; + default: + break; + } + + advance(); + } +} diff --git a/cclox_src/parser/parser.hh b/cclox_src/parser/parser.hh new file mode 100644 index 0000000..0e05aa4 --- /dev/null +++ b/cclox_src/parser/parser.hh @@ -0,0 +1,47 @@ +#ifndef PARSER_HH +#define PARSER_HH + +#include "expr.hh" +#include "../tokenizer.hh" +#include +#include +#include +#include + +struct ParseException : public std::exception { + ParseException() : message{""} {} + ParseException(const std::string& message) : message{message} {} + const char* what() const noexcept override { + return message.c_str(); + } + +private: + std::string message; +}; + +struct Parser { + Parser(std::vector tokens) : tokens{tokens} {} + std::optional parse(); + Expr expression(); + Expr equality(); + Expr comparison(); + Expr term(); + Expr factor(); + Expr unary(); + Expr primary(); + Token advance(); + Token peek(); + Token previous(); + Token consume(TokenType type, std::string &&message); + bool match(std::vector types); + bool check(TokenType type); + bool is_at_end(); + ParseException error(Token token, std::string &message); + ParseException error(Token token, std::string &&message); + void synchronize(); + + std::vector tokens; + size_t current = 0; +}; + +#endif diff --git a/cclox_src/parser/printer.cc b/cclox_src/parser/printer.cc index a885e93..01ac3ab 100644 --- a/cclox_src/parser/printer.cc +++ b/cclox_src/parser/printer.cc @@ -22,6 +22,10 @@ std::string AstPrinter::print(const Expr &expr) { switch (literal->value.type) { case ObjectType::NIL: return "nil"; + case ObjectType::BOOL: { + bool value = std::get(literal->value.value); + return (value ? "true" : "false"); + } case ObjectType::IDENTIFIER: case ObjectType::STRING_LIT: { std::string value = std::get(literal->value.value); diff --git a/cclox_src/scanner/object.cc b/cclox_src/scanner/object.cc index aae991a..e38659c 100644 --- a/cclox_src/scanner/object.cc +++ b/cclox_src/scanner/object.cc @@ -6,6 +6,8 @@ std::ostream &operator<<(std::ostream &os, const ObjectType &type) { case ObjectType::NIL: os << "ObjectType::NIL"; break; + case ObjectType::BOOL: + os << "ObjectType::BOOL"; case ObjectType::IDENTIFIER: os << "ObjectType::IDENTIFIER"; break; @@ -26,6 +28,9 @@ std::ostream &operator<<(std::ostream &os, const Object &obj) { switch (obj.type) { case ObjectType::NIL: break; + case ObjectType::BOOL: + os << ", " << std::to_string(std::get(obj.value)); + break; case ObjectType::IDENTIFIER: case ObjectType::STRING_LIT: os << ", " << std::get(obj.value);