8 changed files with 419 additions and 2 deletions
			
			
		| @ -0,0 +1,148 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2023 Riyyi | ||||
|  * | ||||
|  * SPDX-License-Identifier: MIT | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstdint> // int64_t | ||||
| #include <iostream> | ||||
| #include <span> | ||||
| #include <string_view> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "error.h" | ||||
| #include "ruc/format/color.h" | ||||
| #include "ruc/singleton.h" | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "types.h" | ||||
| 
 | ||||
| namespace blaze { | ||||
| 
 | ||||
| class Environment { | ||||
| public: | ||||
| 	Environment() = default; | ||||
| 	virtual ~Environment() = default; | ||||
| 
 | ||||
| 	ASTNode* lookup(const std::string& symbol) | ||||
| 	{ | ||||
| 		m_current_key = symbol; | ||||
| 		return m_values.find(symbol) != m_values.end() ? m_values[symbol] : nullptr; | ||||
| 	} | ||||
| 
 | ||||
| protected: | ||||
| 	std::string m_current_key; | ||||
| 	std::unordered_map<std::string, ASTNode*> m_values; | ||||
| }; | ||||
| 
 | ||||
| class GlobalEnvironment final : public Environment { | ||||
| public: | ||||
| 	GlobalEnvironment() | ||||
| 	{ | ||||
| 		auto add = [](std::span<ASTNode*> nodes) -> ASTNode* { | ||||
| 			int64_t result = 0; | ||||
| 
 | ||||
| 			for (auto node : nodes) { | ||||
| 				if (!is<Number>(node)) { | ||||
| 					Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node)); | ||||
| 					return nullptr; | ||||
| 				} | ||||
| 
 | ||||
| 				result += static_cast<Number*>(node)->number(); | ||||
| 			} | ||||
| 
 | ||||
| 			return new Number(result); | ||||
| 		}; | ||||
| 
 | ||||
| 		auto sub = [](std::span<ASTNode*> nodes) -> ASTNode* { | ||||
| 			int64_t result = 0; | ||||
| 
 | ||||
| 			if (nodes.size() == 0) { | ||||
| 				return new Number(0); | ||||
| 			} | ||||
| 
 | ||||
| 			for (auto node : nodes) { | ||||
| 				if (!is<Number>(node)) { | ||||
| 					Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node)); | ||||
| 					return nullptr; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// Start with the first number
 | ||||
| 			result += static_cast<Number*>(nodes[0])->number(); | ||||
| 
 | ||||
| 			// Skip the first node
 | ||||
| 			for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) { | ||||
| 				result -= static_cast<Number*>(*it)->number(); | ||||
| 			} | ||||
| 
 | ||||
| 			return new Number(result); | ||||
| 		}; | ||||
| 
 | ||||
| 		auto mul = [](std::span<ASTNode*> nodes) -> ASTNode* { | ||||
| 			int64_t result = 1; | ||||
| 
 | ||||
| 			for (auto node : nodes) { | ||||
| 				if (!is<Number>(node)) { | ||||
| 					Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node)); | ||||
| 					return nullptr; | ||||
| 				} | ||||
| 
 | ||||
| 				result *= static_cast<Number*>(node)->number(); | ||||
| 			} | ||||
| 
 | ||||
| 			return new Number(result); | ||||
| 		}; | ||||
| 
 | ||||
| 		auto div = [this](std::span<ASTNode*> nodes) -> ASTNode* { | ||||
| 			double result = 0; | ||||
| 
 | ||||
| 			if (nodes.size() == 0) { | ||||
| 				Error::the().addError(format("wrong number of arguments: {}, 0", m_current_key)); | ||||
| 				return nullptr; | ||||
| 			} | ||||
| 
 | ||||
| 			for (auto node : nodes) { | ||||
| 				if (!is<Number>(node)) { | ||||
| 					Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node)); | ||||
| 					return nullptr; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// Start with the first number
 | ||||
| 			result += static_cast<Number*>(nodes[0])->number(); | ||||
| 
 | ||||
| 			// Skip the first node
 | ||||
| 			for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) { | ||||
| 				result /= static_cast<Number*>(*it)->number(); | ||||
| 			} | ||||
| 
 | ||||
| 			return new Number((int64_t)result); | ||||
| 		}; | ||||
| 
 | ||||
| 		m_values.emplace("+", new Function(add)); | ||||
| 		m_values.emplace("-", new Function(sub)); | ||||
| 		m_values.emplace("*", new Function(mul)); | ||||
| 		m_values.emplace("/", new Function(div)); | ||||
| 	} | ||||
| 	virtual ~GlobalEnvironment() = default; | ||||
| }; | ||||
| 
 | ||||
| } // namespace blaze
 | ||||
| 
 | ||||
| // associative data structure that maps symbols (the keys) to values
 | ||||
| 
 | ||||
| // values = anything, including other symbols.
 | ||||
| // an environment is like a hash table
 | ||||
| 
 | ||||
| // value can map to:
 | ||||
| // list
 | ||||
| // vector
 | ||||
| // hash-map
 | ||||
| // symbol
 | ||||
| // number
 | ||||
| // string
 | ||||
| // function
 | ||||
| @ -0,0 +1,90 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2023 Riyyi | ||||
|  * | ||||
|  * SPDX-License-Identifier: MIT | ||||
|  */ | ||||
| 
 | ||||
| #include <span> // std::span | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "environment.h" | ||||
| #include "eval.h" | ||||
| #include "ruc/meta/assert.h" | ||||
| #include "types.h" | ||||
| 
 | ||||
| namespace blaze { | ||||
| 
 | ||||
| Eval::Eval(ASTNode* ast, Environment* env) | ||||
| 	: m_ast(ast) | ||||
| 	, m_env(env) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void Eval::eval() | ||||
| { | ||||
| 	m_ast = evalImpl(m_ast, m_env); | ||||
| } | ||||
| 
 | ||||
| ASTNode* Eval::evalImpl(ASTNode* ast, Environment* env) | ||||
| { | ||||
| 	if (!is<List>(ast)) { | ||||
| 		return evalAst(ast, env); | ||||
| 	} | ||||
| 	if (static_cast<List*>(ast)->empty()) { | ||||
| 		return ast; | ||||
| 	} | ||||
| 
 | ||||
| 	return apply(static_cast<List*>(evalAst(ast, env))); | ||||
| } | ||||
| 
 | ||||
| ASTNode* Eval::evalAst(ASTNode* ast, Environment* env) | ||||
| { | ||||
| 	if (is<Symbol>(ast)) { | ||||
| 		auto result = env->lookup(static_cast<Symbol*>(ast)->symbol()); | ||||
| 		if (!result) { | ||||
| 			Error::the().addError(format("'{}' not found", ast)); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
| 	else if (is<List>(ast)) { | ||||
| 		auto result = new List(); | ||||
| 		auto nodes = static_cast<List*>(ast)->nodes(); | ||||
| 		for (auto node : nodes) { | ||||
| 			result->addNode(evalImpl(node, env)); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
| 	else if (is<Vector>(ast)) { | ||||
| 		auto result = new Vector(); | ||||
| 		auto nodes = static_cast<Vector*>(ast)->nodes(); | ||||
| 		for (auto node : nodes) { | ||||
| 			result->addNode(evalImpl(node, env)); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
| 	else if (is<HashMap>(ast)) { | ||||
| 		// TODO
 | ||||
| 		VERIFY_NOT_REACHED(); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	return ast; | ||||
| } | ||||
| 
 | ||||
| ASTNode* Eval::apply(List* evaluated_list) | ||||
| { | ||||
| 	auto nodes = evaluated_list->nodes(); | ||||
| 
 | ||||
| 	if (!is<Function>(nodes[0])) { | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	// car
 | ||||
| 	auto lambda = static_cast<Function*>(nodes[0])->lambda(); | ||||
| 	// cdr
 | ||||
| 	std::span<ASTNode*> span { nodes.data() + 1, nodes.size() - 1 }; | ||||
| 
 | ||||
| 	return lambda(span); | ||||
| } | ||||
| 
 | ||||
| } // namespace blaze
 | ||||
| @ -0,0 +1,32 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2023 Riyyi | ||||
|  * | ||||
|  * SPDX-License-Identifier: MIT | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "environment.h" | ||||
| 
 | ||||
| namespace blaze { | ||||
| 
 | ||||
| class Eval { | ||||
| public: | ||||
| 	Eval(ASTNode* ast, Environment* env); | ||||
| 	virtual ~Eval() = default; | ||||
| 
 | ||||
| 	void eval(); | ||||
| 
 | ||||
| 	ASTNode* ast() const { return m_ast; } | ||||
| 
 | ||||
| private: | ||||
| 	ASTNode* evalImpl(ASTNode* ast, Environment* env); | ||||
| 	ASTNode* evalAst(ASTNode* ast, Environment* env); | ||||
| 	ASTNode* apply(List* evaluated_list); | ||||
| 
 | ||||
| 	ASTNode* m_ast { nullptr }; | ||||
| 	Environment* m_env { nullptr }; | ||||
| }; | ||||
| 
 | ||||
| } // namespace blaze
 | ||||
| @ -0,0 +1,113 @@ | ||||
| #include <csignal>  // std::signal | ||||
| #include <cstdlib>  // std::exit | ||||
| #include <iostream> // std::cin | ||||
| #include <string>   // std::getline | ||||
| #include <string_view> | ||||
| 
 | ||||
| #include "environment.h" | ||||
| #include "eval.h" | ||||
| #include "ruc/argparser.h" | ||||
| #include "ruc/format/color.h" | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "error.h" | ||||
| #include "lexer.h" | ||||
| #include "printer.h" | ||||
| #include "reader.h" | ||||
| #include "settings.h" | ||||
| 
 | ||||
| #if 1 | ||||
| auto read(std::string_view input) -> blaze::ASTNode* | ||||
| { | ||||
| 	blaze::Lexer lexer(input); | ||||
| 	lexer.tokenize(); | ||||
| 	if (blaze::Settings::the().get("dump-lexer") == "1") { | ||||
| 		lexer.dump(); | ||||
| 	} | ||||
| 
 | ||||
| 	blaze::Reader reader(std::move(lexer.tokens())); | ||||
| 	reader.read(); | ||||
| 	if (blaze::Settings::the().get("dump-reader") == "1") { | ||||
| 		reader.dump(); | ||||
| 	} | ||||
| 
 | ||||
| 	return reader.node(); | ||||
| } | ||||
| 
 | ||||
| auto eval(blaze::ASTNode* ast) -> blaze::ASTNode* | ||||
| { | ||||
| 	blaze::GlobalEnvironment env; | ||||
| 	blaze::Eval eval(ast, &env); | ||||
| 	eval.eval(); | ||||
| 	return eval.ast(); | ||||
| } | ||||
| 
 | ||||
| auto print(blaze::ASTNode* exp) -> void | ||||
| { | ||||
| 	blaze::Printer printer(exp); | ||||
| 	printer.dump(); | ||||
| } | ||||
| 
 | ||||
| auto rep(std::string_view input) -> void | ||||
| { | ||||
| 	blaze::Error::the().clearErrors(); | ||||
| 	blaze::Error::the().setInput(input); | ||||
| 
 | ||||
| 	print(eval(read(input))); | ||||
| } | ||||
| 
 | ||||
| static auto cleanup(int signal) -> void | ||||
| { | ||||
| 	print("\033[0m"); | ||||
| 	std::exit(signal); | ||||
| } | ||||
| 
 | ||||
| auto main(int argc, char* argv[]) -> int | ||||
| { | ||||
| 	bool dump_lexer = false; | ||||
| 	bool dump_reader = false; | ||||
| 	bool pretty_print = false; | ||||
| 
 | ||||
| 	// CLI arguments
 | ||||
| 	ruc::ArgParser arg_parser; | ||||
| 	arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr); | ||||
| 	arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr); | ||||
| 	arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr); | ||||
| 	arg_parser.parse(argc, argv); | ||||
| 
 | ||||
| 	// Set settings
 | ||||
| 	blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0"); | ||||
| 	blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0"); | ||||
| 	blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0"); | ||||
| 
 | ||||
| 	// Signal callbacks
 | ||||
| 	std::signal(SIGINT, cleanup); | ||||
| 	std::signal(SIGTERM, cleanup); | ||||
| 
 | ||||
| 	while (true) { | ||||
| 		if (!pretty_print) { | ||||
| 			print("user> "); | ||||
| 		} | ||||
| 		else { | ||||
| 			print(fg(ruc::format::TerminalColor::Blue), "user>"); | ||||
| 			print(" \033[1m"); | ||||
| 		} | ||||
| 		std::string line; | ||||
| 		std::getline(std::cin, line); | ||||
| 		if (pretty_print) { | ||||
| 			print("\033[0m"); | ||||
| 		} | ||||
| 
 | ||||
| 		// Exit with Ctrl-D
 | ||||
| 		if (std::cin.eof() || std::cin.fail()) { | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		rep(line); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| // - Add AST node printing support to ruc::format
 | ||||
					Loading…
					
					
				
		Reference in new issue