Compare commits
	
		
			19 Commits
		
	
	
		
			ae87a3b4a3
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f094fcb376 | |||
| fb06f6f483 | |||
| 4f39417d3e | |||
| 
						 | 
					2690c07220 | ||
| e4723166a2 | |||
| e352df9d7d | |||
| 7b8903c19c | |||
| b3291a9e3a | |||
| 3aec8b6b5b | |||
| 1c1d754750 | |||
| 6f156c0487 | |||
| 1118e825f3 | |||
| 
						 | 
					a6e129eb12 | ||
| 
						 | 
					c6f9307253 | ||
| 
						 | 
					339b92dbae | ||
| 
						 | 
					a57cb5fde0 | ||
| 
						 | 
					c78e694f87 | ||
| 
						 | 
					de85b0d93c | ||
| 56e259d7ed | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,5 @@
 | 
			
		||||
cclox
 | 
			
		||||
clox
 | 
			
		||||
*.dSYM
 | 
			
		||||
.vscode
 | 
			
		||||
.zed
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@@ -13,7 +13,7 @@ CLOX_OUT  = clox
 | 
			
		||||
all: cclox clox
 | 
			
		||||
 | 
			
		||||
cclox: ${CCLOX_SRC}
 | 
			
		||||
	${CXX} ${CFLAGS} ${CCLOX_SRC} -o ${CCLOX_OUT}
 | 
			
		||||
	${CXX} -std=c++20 ${CFLAGS} ${CCLOX_SRC} -o ${CCLOX_OUT}
 | 
			
		||||
 | 
			
		||||
clox: ${CLOX_SRC}
 | 
			
		||||
	${CC} ${CFLAGS} ${CLOX_SRC} -o ${CLOX_OUT}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,24 @@
 | 
			
		||||
#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::runtime_error(const RuntimeException &exception) {
 | 
			
		||||
  std::cout << "RuntimeException [" << exception.token.line << "]: " << exception.what() << '\n';
 | 
			
		||||
  had_runtime_error = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,14 +1,30 @@
 | 
			
		||||
#ifndef ERROR_HANDLER_HH
 | 
			
		||||
#define ERROR_HANDLER_HH
 | 
			
		||||
 | 
			
		||||
#include "tokenizer.hh"
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
struct RuntimeException : public std::exception {
 | 
			
		||||
  RuntimeException(Token token, const std::string& message) : token{token}, message{message} {}
 | 
			
		||||
  const char* what() const noexcept override {
 | 
			
		||||
    return message.c_str();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Token token;
 | 
			
		||||
private:
 | 
			
		||||
  std::string message;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ErrorHandler {
 | 
			
		||||
  ErrorHandler() : had_error{false} {};
 | 
			
		||||
  void error(int line, const std::string &message);
 | 
			
		||||
  void error(Token token, const std::string &message);
 | 
			
		||||
  void runtime_error(const RuntimeException &exception);
 | 
			
		||||
  void report(int line, const std::string &where, const std::string &message);
 | 
			
		||||
 | 
			
		||||
  bool had_error;
 | 
			
		||||
  bool had_runtime_error;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
#include "interpreter.hh"
 | 
			
		||||
#include "error_handler.hh"
 | 
			
		||||
#include "tokenizer.hh"
 | 
			
		||||
#include "parser.hh"
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
@@ -33,6 +35,9 @@ void run_file(const char *path) {
 | 
			
		||||
    if (error_handler.had_error) {
 | 
			
		||||
      exit(EX_DATAERR);
 | 
			
		||||
    }
 | 
			
		||||
    if (error_handler.had_runtime_error) {
 | 
			
		||||
      exit(EX_SOFTWARE);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +48,7 @@ void run_prompt() {
 | 
			
		||||
    run(line);
 | 
			
		||||
    std::cout << PROMPT;
 | 
			
		||||
    error_handler.had_error = false;
 | 
			
		||||
    error_handler.had_runtime_error = false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +56,13 @@ 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::vector<Stmt> statements = parser.parse();
 | 
			
		||||
 | 
			
		||||
  if (error_handler.had_error) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static AstInterpreter interpreter{};
 | 
			
		||||
  interpreter.interpret(statements);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,27 +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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ExprPtr literal = std::make_shared<Expr>(ExprType::LITERAL, Literal{123});
 | 
			
		||||
  ExprPtr left = std::make_shared<Expr>(ExprType::UNARY, Unary{Token{TokenType::MINUS, "-", Object{}, 1}, literal});
 | 
			
		||||
 | 
			
		||||
  Token op{TokenType::STAR, "*", Object{}, 1};
 | 
			
		||||
 | 
			
		||||
  ExprPtr gliteral = std::make_shared<Expr>(ExprType::LITERAL, Literal{45.67});
 | 
			
		||||
  ExprPtr right = std::make_shared<Expr>(ExprType::GROUPING, gliteral);
 | 
			
		||||
 | 
			
		||||
  Expr expr = {ExprType::BINARY, Binary{left, op, right}};
 | 
			
		||||
 | 
			
		||||
  AstPrinter printer{};
 | 
			
		||||
  std::cout << printer.print(expr) << '\n';
 | 
			
		||||
  run_interpreter(argc, argv);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
#ifndef PARSER_CC
 | 
			
		||||
#define PARSER_CC
 | 
			
		||||
#ifndef MAIN_PARSER_CC
 | 
			
		||||
#define MAIN_PARSER_CC
 | 
			
		||||
 | 
			
		||||
#include "parser/expr.cc"
 | 
			
		||||
#include "parser/stmt.cc"
 | 
			
		||||
#include "parser/parser.cc"
 | 
			
		||||
#include "parser/environment.cc"
 | 
			
		||||
#include "parser/ast_printer.cc"
 | 
			
		||||
#include "parser/ast_interpreter.cc"
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
#ifndef PARSER_HH
 | 
			
		||||
#define PARSER_HH
 | 
			
		||||
#ifndef MAIN_PARSER_HH
 | 
			
		||||
#define MAIN_PARSER_HH
 | 
			
		||||
 | 
			
		||||
#include "parser/expr.hh"
 | 
			
		||||
#include "parser/stmt.hh"
 | 
			
		||||
#include "parser/parser.hh"
 | 
			
		||||
#include "parser/environment.hh"
 | 
			
		||||
#include "parser/ast_printer.hh"
 | 
			
		||||
#include "parser/ast_interpreter.hh"
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										242
									
								
								cclox_src/parser/ast_interpreter.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								cclox_src/parser/ast_interpreter.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,242 @@
 | 
			
		||||
#include "ast_interpreter.hh"
 | 
			
		||||
#include "environment.hh"
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include "stmt.hh"
 | 
			
		||||
#include "../error_handler.hh"
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
extern ErrorHandler error_handler;
 | 
			
		||||
 | 
			
		||||
void AstInterpreter::interpret(const std::vector<Stmt> &statements) {
 | 
			
		||||
  try {
 | 
			
		||||
    for (const Stmt & stmt : statements) {
 | 
			
		||||
      execute(stmt);
 | 
			
		||||
    }
 | 
			
		||||
  } catch (RuntimeException e) {
 | 
			
		||||
    error_handler.runtime_error(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AstInterpreter::execute(const Stmt &stmt) {
 | 
			
		||||
  switch (stmt.type) {
 | 
			
		||||
    case StmtType::EXPRESSION: return execute_expression_statement(stmt);
 | 
			
		||||
    case StmtType::PRINT:      return execute_print_statement(stmt);
 | 
			
		||||
    case StmtType::VAR:        return execute_var_statement(stmt);
 | 
			
		||||
    case StmtType::BLOCK: {
 | 
			
		||||
      std::shared_ptr<_Block> ptr = std::get<std::shared_ptr<_Block>>(stmt.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return execute_block_statement(ptr->statements, Environment{std::make_shared<Environment>(environment)});
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AstInterpreter::execute_expression_statement(const Stmt &stmt) {
 | 
			
		||||
  std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(stmt.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
  evaluate(ptr->expr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AstInterpreter::execute_print_statement(const Stmt &stmt) {
 | 
			
		||||
  std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(stmt.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
  Object value{evaluate(ptr->expr)};
 | 
			
		||||
  std::cout << value << '\n';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AstInterpreter::execute_var_statement(const Stmt &stmt) {
 | 
			
		||||
  Object value{};
 | 
			
		||||
  std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(stmt.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
 | 
			
		||||
  if (ptr->expr.type == ExprType::LITERAL) {
 | 
			
		||||
    std::shared_ptr<_Literal> eptr = std::get<std::shared_ptr<_Literal>>(ptr->expr.value);
 | 
			
		||||
    assert(eptr != nullptr);
 | 
			
		||||
    if (eptr->value.type == ObjectType::NIL) {
 | 
			
		||||
      environment.define(ptr->name.lexeme, value);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  value = evaluate(ptr->expr);
 | 
			
		||||
  environment.define(ptr->name.lexeme, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AstInterpreter::execute_block_statement(const std::vector<Stmt> &statements, Environment environment) {
 | 
			
		||||
  Environment previous = this->environment;
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    this->environment = environment;
 | 
			
		||||
    for (const auto &stmt : statements) {
 | 
			
		||||
      execute(stmt);
 | 
			
		||||
    }
 | 
			
		||||
  } catch (std::exception) {}
 | 
			
		||||
 | 
			
		||||
  this->environment = previous;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate(const Expr &expr) {
 | 
			
		||||
  switch (expr.type) {
 | 
			
		||||
    case ExprType::BINARY:   return evaluate_binary_expression(expr);
 | 
			
		||||
    case ExprType::GROUPING: return evaluate_grouping_expression(expr);
 | 
			
		||||
    case ExprType::LITERAL:  return evaluate_literal_expression(expr);
 | 
			
		||||
    case ExprType::UNARY:    return evaluate_unary_expression(expr);
 | 
			
		||||
    case ExprType::VARIABLE: return evaluate_variable_expression(expr);
 | 
			
		||||
    case ExprType::ASSIGN:   return evaluate_assignment_expression(expr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate_binary_expression(const Expr &expr) {
 | 
			
		||||
  std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(expr.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
 | 
			
		||||
  Object left  = evaluate(ptr->left);
 | 
			
		||||
  Object right = evaluate(ptr->right);
 | 
			
		||||
 | 
			
		||||
  if (left.type == ObjectType::NUMBER && right.type == ObjectType::NUMBER) {
 | 
			
		||||
    double left_val = std::get<double>(left.value);
 | 
			
		||||
    double right_val = std::get<double>(right.value);
 | 
			
		||||
 | 
			
		||||
    switch (ptr->op.type) {
 | 
			
		||||
      case TokenType::PLUS:  return left_val + right_val;
 | 
			
		||||
      case TokenType::MINUS: return left_val - right_val;
 | 
			
		||||
      case TokenType::STAR:  return left_val * right_val;
 | 
			
		||||
      case TokenType::SLASH: {
 | 
			
		||||
        if (right_val == 0) {
 | 
			
		||||
          throw RuntimeException{ptr->op, "Division by zero."};
 | 
			
		||||
        }
 | 
			
		||||
        return left_val / right_val;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case TokenType::GREATER:       return left_val > right_val;
 | 
			
		||||
      case TokenType::GREATER_EQUAL: return left_val >= right_val;
 | 
			
		||||
      case TokenType::LESS:          return left_val < right_val;
 | 
			
		||||
      case TokenType::LESS_EQUAL:    return left_val <= right_val;
 | 
			
		||||
 | 
			
		||||
      case TokenType::BANG_EQUAL:  return left_val != right_val;
 | 
			
		||||
      case TokenType::EQUAL_EQUAL: return left_val == right_val;
 | 
			
		||||
 | 
			
		||||
      default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for numbers."};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (left.type == ObjectType::STRING_LIT) {
 | 
			
		||||
    right = Object{StringObjectType::LITERAL, right.to_string()};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (right.type == ObjectType::STRING_LIT) {
 | 
			
		||||
    left = Object{StringObjectType::LITERAL, left.to_string()};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (left.type == ObjectType::STRING_LIT && right.type == ObjectType::STRING_LIT) {
 | 
			
		||||
    std::string left_val = std::get<std::string>(left.value);
 | 
			
		||||
    std::string right_val = std::get<std::string>(right.value);
 | 
			
		||||
 | 
			
		||||
    switch (ptr->op.type) {
 | 
			
		||||
      case TokenType::PLUS: return Object{StringObjectType::LITERAL, left_val + right_val};
 | 
			
		||||
 | 
			
		||||
      case TokenType::BANG_EQUAL:  return left_val != right_val;
 | 
			
		||||
      case TokenType::EQUAL_EQUAL: return left_val == right_val;
 | 
			
		||||
 | 
			
		||||
      default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for strings."};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (left.type == ObjectType::BOOL && right.type == ObjectType::BOOL) {
 | 
			
		||||
    bool left_val = std::get<bool>(left.value);
 | 
			
		||||
    bool right_val = std::get<bool>(right.value);
 | 
			
		||||
 | 
			
		||||
    switch (ptr->op.type) {
 | 
			
		||||
      case TokenType::BANG_EQUAL:  return left_val != right_val;
 | 
			
		||||
      case TokenType::EQUAL_EQUAL: return left_val == right_val;
 | 
			
		||||
 | 
			
		||||
      default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for booleans."};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (left.type == ObjectType::NIL && right.type == ObjectType::NIL) {
 | 
			
		||||
    switch (ptr->op.type) {
 | 
			
		||||
      case TokenType::BANG_EQUAL:  return false;
 | 
			
		||||
      case TokenType::EQUAL_EQUAL: return true;
 | 
			
		||||
 | 
			
		||||
      default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for null."};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (left.type == ObjectType::NIL) {
 | 
			
		||||
    switch (ptr->op.type) {
 | 
			
		||||
      case TokenType::BANG_EQUAL:  return true;
 | 
			
		||||
      case TokenType::EQUAL_EQUAL: return false;
 | 
			
		||||
 | 
			
		||||
      default: break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for operands " + left.to_string() + " and " + right.to_string() + "."};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate_grouping_expression(const Expr &expr) {
 | 
			
		||||
  std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(expr.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
  return evaluate(ptr->expr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate_literal_expression(const Expr &expr) {
 | 
			
		||||
  std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(expr.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
  return ptr->value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate_unary_expression(const Expr &expr) {
 | 
			
		||||
  std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(expr.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
 | 
			
		||||
  Object right = evaluate(ptr->right);
 | 
			
		||||
 | 
			
		||||
  switch (ptr->op.type) {
 | 
			
		||||
    case TokenType::MINUS: {
 | 
			
		||||
      switch (right.type) {
 | 
			
		||||
        case ObjectType::NUMBER: {
 | 
			
		||||
          double value = std::get<double>(right.value);
 | 
			
		||||
          return -value;
 | 
			
		||||
        }
 | 
			
		||||
        default: throw RuntimeException{ptr->op, "Operand to unary operator - must be a number."};
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    case TokenType::BANG:
 | 
			
		||||
      return !is_truthy(right);
 | 
			
		||||
    default: break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return Object{};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate_variable_expression(const Expr &expr) {
 | 
			
		||||
  std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(expr.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
  return environment.get(ptr->name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object AstInterpreter::evaluate_assignment_expression(const Expr &expr) {
 | 
			
		||||
  std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(expr.value);
 | 
			
		||||
  assert(ptr != nullptr);
 | 
			
		||||
  Object value = evaluate(ptr->value);
 | 
			
		||||
  environment.assign(ptr->name, value);
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AstInterpreter::is_truthy(const Object &object) {
 | 
			
		||||
  switch (object.type) {
 | 
			
		||||
    case ObjectType::NIL: return false;
 | 
			
		||||
    case ObjectType::BOOL: {
 | 
			
		||||
      bool value = std::get<bool>(object.value);
 | 
			
		||||
      return value;
 | 
			
		||||
    }
 | 
			
		||||
    default: return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								cclox_src/parser/ast_interpreter.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								cclox_src/parser/ast_interpreter.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
#ifndef AST_INTERPRETER_HH
 | 
			
		||||
#define AST_INTERPRETER_HH
 | 
			
		||||
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include "stmt.hh"
 | 
			
		||||
#include "environment.hh"
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
struct AstInterpreter {
 | 
			
		||||
  void interpret(const std::vector<Stmt> &statements);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void execute(const Stmt &stmt);
 | 
			
		||||
  void execute_expression_statement(const Stmt &stmt);
 | 
			
		||||
  void execute_print_statement(const Stmt &stmt);
 | 
			
		||||
  void execute_var_statement(const Stmt &stmt);
 | 
			
		||||
  void execute_block_statement(const std::vector<Stmt> &statements, Environment environment);
 | 
			
		||||
 | 
			
		||||
  Object evaluate(const Expr &expr);
 | 
			
		||||
  Object evaluate_binary_expression(const Expr &expr);
 | 
			
		||||
  Object evaluate_grouping_expression(const Expr &expr);
 | 
			
		||||
  Object evaluate_literal_expression(const Expr &expr);
 | 
			
		||||
  Object evaluate_unary_expression(const Expr &expr);
 | 
			
		||||
  Object evaluate_variable_expression(const Expr &expr);
 | 
			
		||||
  Object evaluate_assignment_expression(const Expr &expr);
 | 
			
		||||
  bool is_truthy(const Object &object);
 | 
			
		||||
  bool is_equal(const Object &left, const Object &right);
 | 
			
		||||
 | 
			
		||||
  Environment environment{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										51
									
								
								cclox_src/parser/ast_printer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								cclox_src/parser/ast_printer.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
#include "ast_printer.hh"
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
std::string AstPrinter::print(const Expr &expr) {
 | 
			
		||||
  switch (expr.type) {
 | 
			
		||||
    case ExprType::BINARY: {
 | 
			
		||||
      std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return parenthesize(ptr->op.lexeme, {&ptr->left, &ptr->right});
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::GROUPING: {
 | 
			
		||||
      std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return parenthesize("group", {&ptr->expr});
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::LITERAL: {
 | 
			
		||||
      std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return ptr->value.to_string();
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::UNARY: {
 | 
			
		||||
      std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return parenthesize(ptr->op.lexeme, {&ptr->right});
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::VARIABLE: {
 | 
			
		||||
      std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return ptr->name.lexeme;
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::ASSIGN: {
 | 
			
		||||
      std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      return ptr->name.lexeme;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string AstPrinter::parenthesize(const std::string &name, std::vector<const Expr *> exprs) {
 | 
			
		||||
  std::stringstream ss{};
 | 
			
		||||
  ss << '(' << name;
 | 
			
		||||
  for (const Expr *expr : exprs) {
 | 
			
		||||
    ss << ' ' << print(*expr);
 | 
			
		||||
  }
 | 
			
		||||
  ss << ')';
 | 
			
		||||
 | 
			
		||||
  return ss.str();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								cclox_src/parser/ast_printer.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								cclox_src/parser/ast_printer.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#ifndef AST_PRINTER_HH
 | 
			
		||||
#define AST_PRINTER_HH
 | 
			
		||||
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
struct AstPrinter {
 | 
			
		||||
  std::string print(const Expr &expr);
 | 
			
		||||
private:
 | 
			
		||||
  std::string parenthesize(const std::string &name, std::vector<const Expr *> exprs);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										64
									
								
								cclox_src/parser/environment.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								cclox_src/parser/environment.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
#include "environment.hh"
 | 
			
		||||
#include "../error_handler.hh"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
Environment::Environment(const Environment &other) : enclosing{other.enclosing}, values{other.values} {}
 | 
			
		||||
 | 
			
		||||
Environment::Environment(Environment &&other) noexcept 
 | 
			
		||||
  : enclosing{std::move(other.enclosing)}, values{std::move(other.values)} {}
 | 
			
		||||
 | 
			
		||||
Environment &Environment::operator=(const Environment &other) {
 | 
			
		||||
  if (this == &other) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  enclosing = other.enclosing;
 | 
			
		||||
  values    = other.values;
 | 
			
		||||
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Environment &Environment::operator=(Environment &&other) noexcept {
 | 
			
		||||
  if (this == &other) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  enclosing = std::move(other.enclosing);
 | 
			
		||||
  values    = std::move(other.values);
 | 
			
		||||
 | 
			
		||||
  other.enclosing.reset();
 | 
			
		||||
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Environment::define(const std::string &name, const Object &value) {
 | 
			
		||||
  values.insert(std::pair(name, value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Environment::assign(const Token &name, const Object &value) {
 | 
			
		||||
  if (values.find(name.lexeme) != values.end()) {
 | 
			
		||||
    values.at(name.lexeme) = value;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (enclosing != nullptr) {
 | 
			
		||||
    enclosing->assign(name, value);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw RuntimeException(name, "Undefined variable '" + name.lexeme + "'.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object Environment::get(const Token &name) {
 | 
			
		||||
  if (values.find(name.lexeme) != values.end()) {
 | 
			
		||||
    return values.at(name.lexeme);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (enclosing != nullptr) {
 | 
			
		||||
    return enclosing->get(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw RuntimeException(name, "Undefined variable '" + name.lexeme + "'.");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								cclox_src/parser/environment.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								cclox_src/parser/environment.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#ifndef ENVIRONMENT_HH
 | 
			
		||||
#define ENVIRONMENT_HH
 | 
			
		||||
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
struct Environment {
 | 
			
		||||
  Environment() : enclosing{nullptr}, values{} {}
 | 
			
		||||
  Environment(std::shared_ptr<Environment> enclosing) : enclosing{enclosing}, values{} {}
 | 
			
		||||
  Environment(const Environment &other);
 | 
			
		||||
  Environment(Environment &&other) noexcept;
 | 
			
		||||
 | 
			
		||||
  Environment &operator=(const Environment &other);
 | 
			
		||||
  Environment &operator=(Environment &&other) noexcept;
 | 
			
		||||
 | 
			
		||||
  ~Environment() = default;
 | 
			
		||||
 | 
			
		||||
  void define(const std::string &name, const Object &value);
 | 
			
		||||
  void assign(const Token &name, const Object &value);
 | 
			
		||||
  Object get(const Token &name);
 | 
			
		||||
private:
 | 
			
		||||
  std::shared_ptr<Environment> enclosing;
 | 
			
		||||
  std::unordered_map<std::string, Object> values;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,53 +1,35 @@
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
std::string AstPrinter::print(const Expr &expr) {
 | 
			
		||||
  switch (expr.type) {
 | 
			
		||||
    case ExprType::BINARY: {
 | 
			
		||||
      Binary node = std::get<Binary>(expr.value);
 | 
			
		||||
      assert(node.left != NULL && node.right != NULL);
 | 
			
		||||
      return parenthesize(node.op.lexeme, {*node.left, *node.right});
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::GROUPING: {
 | 
			
		||||
      Grouping node = std::get<Grouping>(expr.value);
 | 
			
		||||
      assert(node.expression != NULL);
 | 
			
		||||
      return parenthesize("group", {*node.expression});
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::LITERAL: {
 | 
			
		||||
      Literal node = std::get<Literal>(expr.value);
 | 
			
		||||
      switch (node.value.type) {
 | 
			
		||||
        case ObjectType::NIL:
 | 
			
		||||
          return "nil";
 | 
			
		||||
        case ObjectType::IDENTIFIER:
 | 
			
		||||
        case ObjectType::STRING_LIT: {
 | 
			
		||||
          std::string value = std::get<std::string>(node.value.value);
 | 
			
		||||
          return value;
 | 
			
		||||
        }
 | 
			
		||||
        case ObjectType::NUMBER: {
 | 
			
		||||
          double value = std::get<double>(node.value.value);
 | 
			
		||||
          return std::to_string(value);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    case ExprType::UNARY: {
 | 
			
		||||
      Unary node = std::get<Unary>(expr.value);
 | 
			
		||||
      assert(node.right != NULL);
 | 
			
		||||
      return parenthesize(node.op.lexeme, {*node.right});
 | 
			
		||||
    }
 | 
			
		||||
Expr::Expr(ExprType type, ExprVariant value) : type{type}, value{value} {}
 | 
			
		||||
 | 
			
		||||
// Copy constructor
 | 
			
		||||
Expr::Expr(const Expr &other) : type{other.type}, value{other.value} {}
 | 
			
		||||
 | 
			
		||||
// Move constructor
 | 
			
		||||
Expr::Expr(Expr &&other) noexcept : type{other.type}, value{std::move(other.value)} {}
 | 
			
		||||
 | 
			
		||||
// Copy assignment
 | 
			
		||||
Expr &Expr::operator=(const Expr &other) {
 | 
			
		||||
  if (this == &other) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type  = other.type;
 | 
			
		||||
  value = other.value;
 | 
			
		||||
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string AstPrinter::parenthesize(const std::string &name, std::vector<Expr> exprs) {
 | 
			
		||||
  std::stringstream ss{};
 | 
			
		||||
  ss << '(' << name;
 | 
			
		||||
  for (auto &expr : exprs) {
 | 
			
		||||
    ss << ' ' << print(expr);
 | 
			
		||||
// Move assignment
 | 
			
		||||
Expr &Expr::operator=(Expr &&other) noexcept {
 | 
			
		||||
  if (this == &other) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
  ss << ')';
 | 
			
		||||
 | 
			
		||||
  return ss.str();
 | 
			
		||||
  type  = std::move(other.type);
 | 
			
		||||
  value = std::move(other.value);
 | 
			
		||||
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,60 +3,94 @@
 | 
			
		||||
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
struct Expr;
 | 
			
		||||
 | 
			
		||||
using ExprPtr = std::shared_ptr<Expr>;
 | 
			
		||||
 | 
			
		||||
struct Binary {
 | 
			
		||||
  Binary(ExprPtr left, Token op, ExprPtr right) : left{left}, op{op}, right{right} {};
 | 
			
		||||
 | 
			
		||||
  ExprPtr left;
 | 
			
		||||
  Token op;
 | 
			
		||||
  ExprPtr right;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Grouping {
 | 
			
		||||
  Grouping(ExprPtr expr) : expression{expr} {};
 | 
			
		||||
 | 
			
		||||
  ExprPtr expression;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Literal {
 | 
			
		||||
  Literal(Object value) : value{value} {};
 | 
			
		||||
 | 
			
		||||
  Object value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Unary {
 | 
			
		||||
  Unary(Token op, ExprPtr right) : op{op}, right{right} {};
 | 
			
		||||
 | 
			
		||||
  Token op;
 | 
			
		||||
  ExprPtr right;
 | 
			
		||||
};
 | 
			
		||||
struct _Binary;
 | 
			
		||||
struct _Grouping;
 | 
			
		||||
struct _Literal;
 | 
			
		||||
struct _Unary;
 | 
			
		||||
struct _Variable;
 | 
			
		||||
struct _Assign;
 | 
			
		||||
 | 
			
		||||
enum class ExprType {
 | 
			
		||||
  BINARY,
 | 
			
		||||
  GROUPING,
 | 
			
		||||
  LITERAL,
 | 
			
		||||
  UNARY,
 | 
			
		||||
  VARIABLE,
 | 
			
		||||
  ASSIGN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using Expression = std::variant<Binary, Grouping, Literal, Unary>;
 | 
			
		||||
using ExprVariant = std::variant<
 | 
			
		||||
  std::shared_ptr<_Binary>,
 | 
			
		||||
  std::shared_ptr<_Grouping>,
 | 
			
		||||
  std::shared_ptr<_Literal>,
 | 
			
		||||
  std::shared_ptr<_Unary>,
 | 
			
		||||
  std::shared_ptr<_Variable>,
 | 
			
		||||
  std::shared_ptr<_Assign>
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
#define Binary(LEFT, OP, RIGHT) (Expr{ExprType::BINARY, std::make_shared<_Binary>(LEFT, OP, RIGHT)})
 | 
			
		||||
#define Grouping(EXPR) (Expr{ExprType::GROUPING, std::make_shared<_Grouping>(EXPR)})
 | 
			
		||||
#define Literal(VALUE) (Expr{ExprType::LITERAL, std::make_shared<_Literal>(VALUE)})
 | 
			
		||||
#define Unary(OP, RIGHT) (Expr{ExprType::UNARY, std::make_shared<_Unary>(OP, RIGHT)})
 | 
			
		||||
#define Variable(NAME) (Expr{ExprType::VARIABLE, std::make_shared<_Variable>(NAME)})
 | 
			
		||||
#define Assign(NAME, VALUE) (Expr{ExprType::ASSIGN, std::make_shared<_Assign>(NAME, VALUE)})
 | 
			
		||||
 | 
			
		||||
struct Expr {
 | 
			
		||||
  Expr(ExprType type, Expression value) : type{type}, value{value} {}
 | 
			
		||||
  Expr(ExprType type, ExprVariant value);
 | 
			
		||||
 | 
			
		||||
  // Copy constructor
 | 
			
		||||
  Expr(const Expr &other);
 | 
			
		||||
 | 
			
		||||
  // Move constructor
 | 
			
		||||
  Expr(Expr &&other) noexcept;
 | 
			
		||||
 | 
			
		||||
  // Copy assignment
 | 
			
		||||
  Expr &operator=(const Expr &other);
 | 
			
		||||
 | 
			
		||||
  // Move assignment
 | 
			
		||||
  Expr &operator=(Expr &&other) noexcept;
 | 
			
		||||
 | 
			
		||||
  ~Expr() = default;
 | 
			
		||||
 | 
			
		||||
  ExprType type;
 | 
			
		||||
  Expression value;
 | 
			
		||||
  ExprVariant value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AstPrinter {
 | 
			
		||||
public:
 | 
			
		||||
  std::string print(const Expr &expr);
 | 
			
		||||
private:
 | 
			
		||||
  std::string parenthesize(const std::string &name, std::vector<Expr> exprs);
 | 
			
		||||
struct _Binary {
 | 
			
		||||
  _Binary(const Expr &left, Token op, const Expr &right) : left{left}, op{op}, right{right} {}
 | 
			
		||||
  Expr left;
 | 
			
		||||
  Token op;
 | 
			
		||||
  Expr right;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Grouping {
 | 
			
		||||
  _Grouping(const Expr &expr) : expr{expr} {}
 | 
			
		||||
  Expr expr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Literal {
 | 
			
		||||
  _Literal(Object value) : value{value} {}
 | 
			
		||||
  Object value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Unary {
 | 
			
		||||
  _Unary(Token op, const Expr &right) : op{op}, right{right} {}
 | 
			
		||||
  Token op;
 | 
			
		||||
  Expr right;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Variable {
 | 
			
		||||
  _Variable(Token name) : name{name} {}
 | 
			
		||||
  Token name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Assign {
 | 
			
		||||
  _Assign(Token name, const Expr &value) : name{name}, value{value} {}
 | 
			
		||||
  Token name;
 | 
			
		||||
  Expr value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										272
									
								
								cclox_src/parser/parser.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								cclox_src/parser/parser.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
			
		||||
#include "parser.hh"
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include "stmt.hh"
 | 
			
		||||
#include "../error_handler.hh"
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
 | 
			
		||||
extern ErrorHandler error_handler;
 | 
			
		||||
 | 
			
		||||
std::vector<Stmt> Parser::parse() {
 | 
			
		||||
  std::vector<Stmt> statements{};
 | 
			
		||||
  while (!is_at_end()) {
 | 
			
		||||
    statements.push_back(declaration());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return statements;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stmt Parser::declaration() {
 | 
			
		||||
  try {
 | 
			
		||||
    if (match({TokenType::VAR})) {
 | 
			
		||||
      return var_declaration();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return statement();
 | 
			
		||||
  } catch (ParseException e) {
 | 
			
		||||
    synchronize();
 | 
			
		||||
    return Expression(Literal(Object{}));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stmt Parser::var_declaration() {
 | 
			
		||||
  Token name       = consume(TokenType::IDENTIFIER, "Expect variable name.");
 | 
			
		||||
  Expr initializer = Literal(Object{});
 | 
			
		||||
 | 
			
		||||
  if (match({TokenType::EQUAL})) {
 | 
			
		||||
    initializer = expression();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  consume(TokenType::SEMICOLON, "Expect ';' after variable declaration");
 | 
			
		||||
  return Var(name, initializer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stmt Parser::statement() {
 | 
			
		||||
  if (match({TokenType::PRINT})) {
 | 
			
		||||
    return print_statement();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (match({TokenType::LEFT_BRACE})) {
 | 
			
		||||
    return Block(block_statement());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return expression_statement();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Stmt> Parser::block_statement() {
 | 
			
		||||
  std::vector<Stmt> statements{};
 | 
			
		||||
 | 
			
		||||
  while (!check(TokenType::RIGHT_BRACE) && !is_at_end()) {
 | 
			
		||||
    statements.push_back(declaration());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  consume(TokenType::RIGHT_BRACE, "Expect '}' after block.");
 | 
			
		||||
  return statements;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stmt Parser::print_statement() {
 | 
			
		||||
  Expr value{expression()};
 | 
			
		||||
  consume(TokenType::SEMICOLON, "Expect ';' after value.");
 | 
			
		||||
  return Print(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stmt Parser::expression_statement() {
 | 
			
		||||
  Expr value{expression()};
 | 
			
		||||
  consume(TokenType::SEMICOLON, "Expect ';' after value.");
 | 
			
		||||
  return Expression(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Expr Parser::expression() {
 | 
			
		||||
  return assignment();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Expr Parser::assignment() {
 | 
			
		||||
  Expr expr = equality();
 | 
			
		||||
 | 
			
		||||
  if (match({TokenType::EQUAL})) {
 | 
			
		||||
    Token equals = previous();
 | 
			
		||||
    Expr value   = assignment();
 | 
			
		||||
 | 
			
		||||
    if (expr.type == ExprType::VARIABLE) {
 | 
			
		||||
      std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(expr.value);
 | 
			
		||||
      assert(ptr != nullptr);
 | 
			
		||||
      Token name = ptr->name;
 | 
			
		||||
      return Assign(name, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error_handler.error(equals, "Invalid assignment target.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return expr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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::IDENTIFIER})) {
 | 
			
		||||
    return Variable(previous());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								cclox_src/parser/parser.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								cclox_src/parser/parser.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
#ifndef PARSER_HH
 | 
			
		||||
#define PARSER_HH
 | 
			
		||||
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include "stmt.hh"
 | 
			
		||||
#include "../tokenizer.hh"
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <exception>
 | 
			
		||||
#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::vector<Stmt> parse();
 | 
			
		||||
  Stmt declaration();
 | 
			
		||||
  Stmt var_declaration();
 | 
			
		||||
  Stmt statement();
 | 
			
		||||
  std::vector<Stmt> block_statement();
 | 
			
		||||
  Stmt print_statement();
 | 
			
		||||
  Stmt expression_statement();
 | 
			
		||||
  Expr expression();
 | 
			
		||||
  Expr assignment();
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										35
									
								
								cclox_src/parser/stmt.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								cclox_src/parser/stmt.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
#include "stmt.hh"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
Stmt::Stmt(StmtType type, StmtVariant value) : type{type}, value{value} {}
 | 
			
		||||
 | 
			
		||||
// Copy constructor
 | 
			
		||||
Stmt::Stmt(const Stmt &other) : type{other.type}, value{other.value} {}
 | 
			
		||||
 | 
			
		||||
// Move constructor
 | 
			
		||||
Stmt::Stmt(Stmt &&other) noexcept : type{other.type}, value{std::move(other.value)} {}
 | 
			
		||||
 | 
			
		||||
// Copy assignment
 | 
			
		||||
Stmt &Stmt::operator=(const Stmt &other) {
 | 
			
		||||
  if (this == &other) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type  = other.type;
 | 
			
		||||
  value = other.value;
 | 
			
		||||
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Move assignment
 | 
			
		||||
Stmt &Stmt::operator=(Stmt &&other) noexcept {
 | 
			
		||||
  if (this == &other) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type  = std::move(other.type);
 | 
			
		||||
  value = std::move(other.value);
 | 
			
		||||
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								cclox_src/parser/stmt.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								cclox_src/parser/stmt.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
#ifndef STMT_HH
 | 
			
		||||
#define STMT_HH
 | 
			
		||||
 | 
			
		||||
#include "expr.hh"
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <variant>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
struct Stmt;
 | 
			
		||||
struct _Expression;
 | 
			
		||||
struct _Print;
 | 
			
		||||
struct _Var;
 | 
			
		||||
struct _Block;
 | 
			
		||||
 | 
			
		||||
enum class StmtType {
 | 
			
		||||
  EXPRESSION,
 | 
			
		||||
  PRINT,
 | 
			
		||||
  VAR,
 | 
			
		||||
  BLOCK,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using StmtVariant = std::variant<
 | 
			
		||||
  std::shared_ptr<_Expression>,
 | 
			
		||||
  std::shared_ptr<_Print>,
 | 
			
		||||
  std::shared_ptr<_Var>,
 | 
			
		||||
  std::shared_ptr<_Block>
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
#define Expression(EXPR) (Stmt{StmtType::EXPRESSION, std::make_shared<_Expression>(EXPR)})
 | 
			
		||||
#define Print(EXPR) (Stmt{StmtType::PRINT, std::make_shared<_Print>(EXPR)})
 | 
			
		||||
#define Var(NAME, EXPR) (Stmt{StmtType::VAR, std::make_shared<_Var>(NAME, EXPR)})
 | 
			
		||||
#define Block(STATEMENTS) (Stmt{StmtType::BLOCK, std::make_shared<_Block>(STATEMENTS)})
 | 
			
		||||
 | 
			
		||||
struct Stmt {
 | 
			
		||||
  Stmt(StmtType type, StmtVariant value);
 | 
			
		||||
 | 
			
		||||
  // Copy constructor
 | 
			
		||||
  Stmt(const Stmt &other);
 | 
			
		||||
 | 
			
		||||
  // Move constructor
 | 
			
		||||
  Stmt(Stmt &&other) noexcept;
 | 
			
		||||
 | 
			
		||||
  // Copy assignment
 | 
			
		||||
  Stmt &operator=(const Stmt &other);
 | 
			
		||||
 | 
			
		||||
  // Move assignment
 | 
			
		||||
  Stmt &operator=(Stmt &&other) noexcept;
 | 
			
		||||
 | 
			
		||||
  ~Stmt() = default;
 | 
			
		||||
 | 
			
		||||
  StmtType type;
 | 
			
		||||
  StmtVariant value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Expression {
 | 
			
		||||
  _Expression(const Expr &expr) : expr{expr} {}
 | 
			
		||||
  Expr expr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Print {
 | 
			
		||||
  _Print(const Expr &expr) : expr{expr} {}
 | 
			
		||||
  Expr expr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Var {
 | 
			
		||||
  _Var(const Token &name, const Expr &expr) : name{name}, expr{expr} {}
 | 
			
		||||
  Token name;
 | 
			
		||||
  Expr expr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _Block {
 | 
			
		||||
  _Block(const std::vector<Stmt> &statements) : statements{statements} {}
 | 
			
		||||
  std::vector<Stmt> statements;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,11 +1,68 @@
 | 
			
		||||
#include "object.hh"
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
std::string Object::to_string() const {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case ObjectType::NIL:
 | 
			
		||||
      return "nil";
 | 
			
		||||
    case ObjectType::BOOL: {
 | 
			
		||||
      bool val = std::get<bool>(value);
 | 
			
		||||
      return (val ? "true" : "false");
 | 
			
		||||
    }
 | 
			
		||||
    case ObjectType::IDENTIFIER:
 | 
			
		||||
    case ObjectType::STRING_LIT: {
 | 
			
		||||
      std::string val = std::get<std::string>(value);
 | 
			
		||||
      return val;
 | 
			
		||||
    }
 | 
			
		||||
    case ObjectType::NUMBER: {
 | 
			
		||||
      double val = std::get<double>(value);
 | 
			
		||||
      std::string output{std::to_string(val)};
 | 
			
		||||
      if (val == ((int)val)) {
 | 
			
		||||
        size_t decimal_index{output.find(".")};
 | 
			
		||||
        output = output.substr(0, decimal_index);
 | 
			
		||||
      }
 | 
			
		||||
      return output;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Object::operator==(const Object &other) {
 | 
			
		||||
  if (type != other.type) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case ObjectType::NIL: return true;
 | 
			
		||||
    case ObjectType::BOOL: {
 | 
			
		||||
      bool left  = std::get<bool>(value);
 | 
			
		||||
      bool right = std::get<bool>(other.value);
 | 
			
		||||
 | 
			
		||||
      return left == right;
 | 
			
		||||
    }
 | 
			
		||||
    case ObjectType::IDENTIFIER:
 | 
			
		||||
    case ObjectType::STRING_LIT: {
 | 
			
		||||
      std::string left  = std::get<std::string>(value);
 | 
			
		||||
      std::string right = std::get<std::string>(other.value);
 | 
			
		||||
 | 
			
		||||
      return left == right;
 | 
			
		||||
    }
 | 
			
		||||
    case ObjectType::NUMBER: {
 | 
			
		||||
      double left  = std::get<double>(value);
 | 
			
		||||
      double right = std::get<double>(other.value);
 | 
			
		||||
 | 
			
		||||
      return left == right;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream &operator<<(std::ostream &os, const ObjectType &type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case ObjectType::NIL:
 | 
			
		||||
      os << "ObjectType::NIL";
 | 
			
		||||
      break;
 | 
			
		||||
    case ObjectType::BOOL:
 | 
			
		||||
      os << "ObjectType::BOOL";
 | 
			
		||||
    case ObjectType::IDENTIFIER:
 | 
			
		||||
      os << "ObjectType::IDENTIFIER";
 | 
			
		||||
      break;
 | 
			
		||||
@@ -21,21 +78,10 @@ std::ostream &operator<<(std::ostream &os, const ObjectType &type) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream &operator<<(std::ostream &os, const Object &obj) {
 | 
			
		||||
  os << "Object(" << obj.type;
 | 
			
		||||
 | 
			
		||||
  switch (obj.type) {
 | 
			
		||||
    case ObjectType::NIL:
 | 
			
		||||
      break;
 | 
			
		||||
    case ObjectType::IDENTIFIER:
 | 
			
		||||
    case ObjectType::STRING_LIT:
 | 
			
		||||
      os << ", " << std::get<std::string>(obj.value);
 | 
			
		||||
      break;
 | 
			
		||||
    case ObjectType::NUMBER:
 | 
			
		||||
      os << ", " << std::get<double>(obj.value);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  os << ')';
 | 
			
		||||
 | 
			
		||||
  os << obj.to_string();
 | 
			
		||||
  return os;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Object::operator!=(const Object &other) {
 | 
			
		||||
  return !operator==(other);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ enum class StringObjectType : uint8_t {
 | 
			
		||||
 | 
			
		||||
enum class ObjectType : uint8_t {
 | 
			
		||||
  NIL,
 | 
			
		||||
  BOOL,
 | 
			
		||||
  IDENTIFIER,
 | 
			
		||||
  STRING_LIT,
 | 
			
		||||
  NUMBER,
 | 
			
		||||
@@ -19,6 +20,7 @@ enum class ObjectType : uint8_t {
 | 
			
		||||
 | 
			
		||||
struct Object {
 | 
			
		||||
  Object() : type{ObjectType::NIL}, value{std::monostate{}} {};
 | 
			
		||||
  Object(bool value) : type{ObjectType::BOOL}, value{value} {};
 | 
			
		||||
  Object(double number) : type{ObjectType::NUMBER}, value{number} {};
 | 
			
		||||
  Object(StringObjectType string_type, std::string value)
 | 
			
		||||
    : type{
 | 
			
		||||
@@ -27,9 +29,12 @@ struct Object {
 | 
			
		||||
        ObjectType::STRING_LIT
 | 
			
		||||
      },
 | 
			
		||||
      value{std::move(value)} {};
 | 
			
		||||
  std::string to_string() const;
 | 
			
		||||
  bool operator==(const Object &other);
 | 
			
		||||
  bool operator!=(const Object &other);
 | 
			
		||||
 | 
			
		||||
  ObjectType type;
 | 
			
		||||
  std::variant<std::monostate, std::string, double> value;
 | 
			
		||||
  std::variant<std::monostate, std::string, double, bool> value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::ostream &operator<<(std::ostream &os, const ObjectType &type);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user