Finish parsing expressions
This commit is contained in:
186
cclox_src/parser/parser.cc
Normal file
186
cclox_src/parser/parser.cc
Normal 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();
|
||||
}
|
||||
}
|
||||
47
cclox_src/parser/parser.hh
Normal file
47
cclox_src/parser/parser.hh
Normal 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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user