Finish parsing expressions
This commit is contained in:
parent
6f156c0487
commit
1c1d754750
@ -1,10 +1,19 @@
|
|||||||
#include "error_handler.hh"
|
#include "error_handler.hh"
|
||||||
|
#include "tokenizer.hh"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void ErrorHandler::error(int line, const std::string &message) {
|
void ErrorHandler::error(int line, const std::string &message) {
|
||||||
report(line, "", 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) {
|
void ErrorHandler::report(int line, const std::string &where, const std::string &message) {
|
||||||
std::cout << "[line " << line << "] Error" << where << ": " << message << '\n';
|
std::cout << "[line " << line << "] Error" << where << ": " << message << '\n';
|
||||||
had_error = true;
|
had_error = true;
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#ifndef ERROR_HANDLER_HH
|
#ifndef ERROR_HANDLER_HH
|
||||||
#define ERROR_HANDLER_HH
|
#define ERROR_HANDLER_HH
|
||||||
|
|
||||||
|
#include "tokenizer.hh"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
struct ErrorHandler {
|
struct ErrorHandler {
|
||||||
ErrorHandler() : had_error{false} {};
|
ErrorHandler() : had_error{false} {};
|
||||||
void error(int line, const std::string &message);
|
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);
|
void report(int line, const std::string &where, const std::string &message);
|
||||||
|
|
||||||
bool had_error;
|
bool had_error;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#include "interpreter.hh"
|
#include "interpreter.hh"
|
||||||
#include "error_handler.hh"
|
#include "error_handler.hh"
|
||||||
#include "tokenizer.hh"
|
#include "tokenizer.hh"
|
||||||
|
#include "parser.hh"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
|
|
||||||
#define PROMPT "> "
|
#define PROMPT "> "
|
||||||
@ -50,7 +52,11 @@ void run(const std::string &code) {
|
|||||||
Scanner scanner{code};
|
Scanner scanner{code};
|
||||||
std::vector<Token> tokens = scanner.scan_tokens();
|
std::vector<Token> tokens = scanner.scan_tokens();
|
||||||
|
|
||||||
for (const auto &token : tokens) {
|
Parser parser{tokens};
|
||||||
std::cout << token << '\n';
|
std::optional<Expr> expression = parser.parse();
|
||||||
|
|
||||||
|
if (expression) {
|
||||||
|
AstPrinter printer{};
|
||||||
|
std::cout << printer.print(*expression) << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,26 +8,12 @@
|
|||||||
ErrorHandler error_handler{};
|
ErrorHandler error_handler{};
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
(void)argc;
|
if (argc > 2) {
|
||||||
(void)argv;
|
std::cout << "Usage: cclox [script]\n";
|
||||||
// if (argc > 2) {
|
exit(EX_USAGE);
|
||||||
// std::cout << "Usage: cclox [script]\n";
|
}
|
||||||
// exit(EX_USAGE);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// run_interpreter(argc, argv);
|
|
||||||
|
|
||||||
Token unary_op{TokenType::MINUS, "-", Object{}, 1};
|
run_interpreter(argc, argv);
|
||||||
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';
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
|
|
||||||
#include "parser/expr.cc"
|
#include "parser/expr.cc"
|
||||||
#include "parser/printer.cc"
|
#include "parser/printer.cc"
|
||||||
|
#include "parser/parser.cc"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#ifndef PARSER_HH
|
#ifndef MAIN_PARSER_HH
|
||||||
#define PARSER_HH
|
#define MAIN_PARSER_HH
|
||||||
|
|
||||||
#include "parser/expr.hh"
|
#include "parser/expr.hh"
|
||||||
#include "parser/printer.hh"
|
#include "parser/printer.hh"
|
||||||
|
#include "parser/parser.hh"
|
||||||
|
|
||||||
#endif
|
#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) {
|
switch (literal->value.type) {
|
||||||
case ObjectType::NIL:
|
case ObjectType::NIL:
|
||||||
return "nil";
|
return "nil";
|
||||||
|
case ObjectType::BOOL: {
|
||||||
|
bool value = std::get<bool>(literal->value.value);
|
||||||
|
return (value ? "true" : "false");
|
||||||
|
}
|
||||||
case ObjectType::IDENTIFIER:
|
case ObjectType::IDENTIFIER:
|
||||||
case ObjectType::STRING_LIT: {
|
case ObjectType::STRING_LIT: {
|
||||||
std::string value = std::get<std::string>(literal->value.value);
|
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:
|
case ObjectType::NIL:
|
||||||
os << "ObjectType::NIL";
|
os << "ObjectType::NIL";
|
||||||
break;
|
break;
|
||||||
|
case ObjectType::BOOL:
|
||||||
|
os << "ObjectType::BOOL";
|
||||||
case ObjectType::IDENTIFIER:
|
case ObjectType::IDENTIFIER:
|
||||||
os << "ObjectType::IDENTIFIER";
|
os << "ObjectType::IDENTIFIER";
|
||||||
break;
|
break;
|
||||||
@ -26,6 +28,9 @@ std::ostream &operator<<(std::ostream &os, const Object &obj) {
|
|||||||
switch (obj.type) {
|
switch (obj.type) {
|
||||||
case ObjectType::NIL:
|
case ObjectType::NIL:
|
||||||
break;
|
break;
|
||||||
|
case ObjectType::BOOL:
|
||||||
|
os << ", " << std::to_string(std::get<bool>(obj.value));
|
||||||
|
break;
|
||||||
case ObjectType::IDENTIFIER:
|
case ObjectType::IDENTIFIER:
|
||||||
case ObjectType::STRING_LIT:
|
case ObjectType::STRING_LIT:
|
||||||
os << ", " << std::get<std::string>(obj.value);
|
os << ", " << std::get<std::string>(obj.value);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user