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