Finish parsing expressions

This commit is contained in:
Abdelrahman Said 2025-06-28 22:51:02 +01:00
parent 6f156c0487
commit 1c1d754750
10 changed files with 270 additions and 23 deletions

View File

@ -1,10 +1,19 @@
#include "error_handler.hh"
#include "tokenizer.hh"
#include <iostream>
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;

View File

@ -1,11 +1,13 @@
#ifndef ERROR_HANDLER_HH
#define ERROR_HANDLER_HH
#include "tokenizer.hh"
#include <string>
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;

View File

@ -1,9 +1,11 @@
#include "interpreter.hh"
#include "error_handler.hh"
#include "tokenizer.hh"
#include "parser.hh"
#include <cstdint>
#include <fstream>
#include <iostream>
#include <optional>
#include <sysexits.h>
#define PROMPT "> "
@ -50,7 +52,11 @@ void run(const std::string &code) {
Scanner scanner{code};
std::vector<Token> tokens = scanner.scan_tokens();
for (const auto &token : tokens) {
std::cout << token << '\n';
Parser parser{tokens};
std::optional<Expr> expression = parser.parse();
if (expression) {
AstPrinter printer{};
std::cout << printer.print(*expression) << '\n';
}
}

View File

@ -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;
}

View File

@ -3,5 +3,6 @@
#include "parser/expr.cc"
#include "parser/printer.cc"
#include "parser/parser.cc"
#endif

View File

@ -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

186
cclox_src/parser/parser.cc Normal file
View File

@ -0,0 +1,186 @@
#include "parser.hh"
#include "expr.hh"
#include "../error_handler.hh"
#include "../tokenizer.hh"
#include <optional>
extern ErrorHandler error_handler;
std::optional<Expr> 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<TokenType> 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();
}
}

View File

@ -0,0 +1,47 @@
#ifndef PARSER_HH
#define PARSER_HH
#include "expr.hh"
#include "../tokenizer.hh"
#include <cstddef>
#include <exception>
#include <optional>
#include <vector>
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<Token> tokens) : tokens{tokens} {}
std::optional<Expr> 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<TokenType> 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<Token> tokens;
size_t current = 0;
};
#endif

View File

@ -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<bool>(literal->value.value);
return (value ? "true" : "false");
}
case ObjectType::IDENTIFIER:
case ObjectType::STRING_LIT: {
std::string value = std::get<std::string>(literal->value.value);

View File

@ -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<bool>(obj.value));
break;
case ObjectType::IDENTIFIER:
case ObjectType::STRING_LIT:
os << ", " << std::get<std::string>(obj.value);