Finish parsing expressions
This commit is contained in:
parent
6f156c0487
commit
1c1d754750
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -3,5 +3,6 @@
|
||||
|
||||
#include "parser/expr.cc"
|
||||
#include "parser/printer.cc"
|
||||
#include "parser/parser.cc"
|
||||
|
||||
#endif
|
||||
|
@ -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
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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user