Finish parsing expressions
This commit is contained in:
		@@ -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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user