From dbfdde72ab665854350d17bdc6c6661c604b1ad4 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 17 Nov 2021 19:19:49 +0100 Subject: [PATCH] version 0.1 --- CMakeLists.txt | 2 + biscuit_doc.odt | Bin 13876 -> 0 bytes doc/biscuit_doc.odt | Bin 0 -> 17733 bytes doc/biscuit_doc.pdf | Bin 0 -> 35123 bytes examples/faculty.bisc | 9 ++ examples/fibonacci.bisc | 11 ++ examples/guessthenumber.bisc | 13 ++ examples/helloworld.bisc | 4 +- examples/numbers.bisc | 5 + examples/test.bisc | 11 ++ src/common.hpp | 12 ++ src/exceptions.hpp | 21 ++- src/keywords.cpp | 273 ++++++++++++++++++++++++++++++++++- src/keywords.hpp | 6 + src/main.cpp | 17 +-- src/parse.cpp | 167 +++++++++++++++++---- src/parse.hpp | 8 +- src/runtime.cpp | 150 +++++++++++++++++++ src/runtime.hpp | 45 ++++++ src/util.cpp | 41 ++++++ 20 files changed, 740 insertions(+), 55 deletions(-) delete mode 100644 biscuit_doc.odt create mode 100644 doc/biscuit_doc.odt create mode 100644 doc/biscuit_doc.pdf create mode 100644 examples/faculty.bisc create mode 100644 examples/fibonacci.bisc create mode 100644 examples/guessthenumber.bisc create mode 100644 examples/numbers.bisc create mode 100644 examples/test.bisc create mode 100644 src/runtime.cpp create mode 100644 src/runtime.hpp create mode 100644 src/util.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e8b3e43..ab1d2d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,8 @@ add_executable(biscuit_interpreter src/common.hpp) target_sources( biscuit_interpreter PRIVATE src/main.cpp src/parse.cpp + src/runtime.cpp + src/util.cpp src/keywords.cpp ) include_directories(${CMAKE_SOURCE_DIR}) diff --git a/biscuit_doc.odt b/biscuit_doc.odt deleted file mode 100644 index 3036d759dcec47b91128df9f9ba62bae13745598..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13876 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 + enum TokenType { KEYWORD, @@ -16,4 +18,14 @@ enum ValueType ANY }; +// some prototypes +struct Token; +class Instruction; +struct Keyword; +class Runtime; + +const std::string resolve_TokenType_str(enum TokenType); +const std::string resolve_ValueType_str(enum ValueType); + + #endif //BISCUIT_INTERPRETER_COMMON_HPP diff --git a/src/exceptions.hpp b/src/exceptions.hpp index 286b782..48d806c 100644 --- a/src/exceptions.hpp +++ b/src/exceptions.hpp @@ -4,6 +4,7 @@ #include #include "common.hpp" +// Parsetime errors struct MalformedTokenExcept : public std::exception { std::string malformed_str; @@ -34,12 +35,26 @@ struct WrongTokenExcept : public std::exception : expected(_expected), got(_got), keyword_name(_keyword_name), token_str(_token_str) {} }; + +// Runtime errors struct TypeErrorExcept : public std::exception { enum ValueType expected, got; - std::string keyword_name, token_str; - inline TypeErrorExcept(const std::string& _keyword_name, const std::string& _token_str, const enum ValueType& _expected, const enum ValueType& _got) - : expected(_expected), got(_got), keyword_name(_keyword_name), token_str(_token_str) {} + std::string token_str; + inline TypeErrorExcept(const std::string& _token_str, const enum ValueType& _expected, const enum ValueType& _got) + : expected(_expected), got(_got), token_str(_token_str) {} +}; + +struct UndeclaredVariableExcept : public std::exception +{ + std::string identifier_str; + inline UndeclaredVariableExcept(const std::string& str) : identifier_str(str) {} +}; + +struct JumpOutOfBoundsExcept : public std::exception +{ + int from, to; + inline JumpOutOfBoundsExcept(int _from, int _to) : from(_from), to(_to) {} }; #endif //BISCUIT_INTERPRETER_EXCEPTIONS_HPP diff --git a/src/keywords.cpp b/src/keywords.cpp index 7612481..65429f3 100644 --- a/src/keywords.cpp +++ b/src/keywords.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include "common.hpp" #include "keywords.hpp" @@ -10,10 +12,277 @@ const std::vector keywords = .name = "print", .expected_num_args = -1, .expected_token_types = {TokenType::ID_OR_LIT}, - .expected_value_types = {ValueType::ANY} + .expected_value_types = {ValueType::ANY}, + .func = [](Runtime& rt, const std::vector& tokens) + { + for(auto& arg : tokens) + { + if(arg.type == TokenType::LITERAL) + { + if(arg.value_type == ValueType::STRING) + std::cout << arg.val_string; + else + std::cout << arg.val_number; + } + else if(arg.type == TokenType::IDENTIFIER) + { + if(rt.resolve_var_type(arg.val_string) == ValueType::STRING) + std::cout << rt.resolve_var_str(arg.val_string); + else if (rt.resolve_var_type(arg.val_string) == ValueType::NUMBER) + std::cout << rt.resolve_var_num(arg.val_string); + } + } + std::cout << std::endl; + } }, { .name = "exit", - .expected_num_args = 0 + .expected_num_args = 0, + .func = [](Runtime& rt, const std::vector& tokens) + { + std::exit(EXIT_SUCCESS); + } + }, + { + .name = "assign", + .expected_num_args = 2, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::ANY, ValueType::ANY}, + .func = [](Runtime& rt, const std::vector& tokens) + { + if(tokens[0].type == TokenType::LITERAL) + { + if(tokens[0].value_type == ValueType::NUMBER) + rt.assign_var_num(tokens[1].val_string, tokens[0].val_number); + else + rt.assign_var_str(tokens[1].val_string, tokens[0].val_string); + } + else if(tokens[0].type == TokenType::IDENTIFIER) + { + if(rt.resolve_var_type(tokens[0].val_string) == ValueType::NUMBER) + rt.assign_var_num(tokens[1].val_string, rt.resolve_var_num(tokens[0].val_string)); + else + rt.assign_var_str(tokens[1].val_string, rt.resolve_var_str(tokens[0].val_string)); + } + } + }, + { + .name = "add", + .expected_num_args = 3, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + float operands[2], res; + + // Retrieve operands + for(unsigned int i = 0; i < 2; i++) + { + if(tokens[i].type == TokenType::LITERAL) + operands[i] = tokens[i].val_number; + else + operands[i] = rt.resolve_var_num(tokens[i].val_string); + } + + res = operands[0] + operands[1]; + + rt.assign_var_num(tokens[2].val_string, res); + } + }, + { + .name = "mul", + .expected_num_args = 3, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + float operands[2], res; + + // Retrieve operands + for(unsigned int i = 0; i < 2; i++) + { + if(tokens[i].type == TokenType::LITERAL) + operands[i] = tokens[i].val_number; + else + operands[i] = rt.resolve_var_num(tokens[i].val_string); + } + + res = operands[0] * operands[1]; + + rt.assign_var_num(tokens[2].val_string, res); + } + }, + { + .name = "div", + .expected_num_args = 3, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + float operands[2], res; + + // Retrieve operands + for(unsigned int i = 0; i < 2; i++) + { + if(tokens[i].type == TokenType::LITERAL) + operands[i] = tokens[i].val_number; + else + operands[i] = rt.resolve_var_num(tokens[i].val_string); + } + + res = operands[0] / operands[1]; + + rt.assign_var_num(tokens[2].val_string, res); + } + }, + { + .name = "sub", + .expected_num_args = 3, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + float operands[2], res; + + // Retrieve operands + for(unsigned int i = 0; i < 2; i++) + { + if(tokens[i].type == TokenType::LITERAL) + operands[i] = tokens[i].val_number; + else + operands[i] = rt.resolve_var_num(tokens[i].val_string); + } + + res = operands[0] - operands[1]; + + rt.assign_var_num(tokens[2].val_string, res); + } + }, + { + .name = "goto", + .expected_num_args = 2, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + int dest; + float condition; + + if(tokens[0].type == TokenType::LITERAL) + dest = std::floor(tokens[0].val_number); + else + dest = std::floor(rt.resolve_var_num(tokens[0].val_string)); + + if(tokens[1].type == TokenType::LITERAL) + condition = std::floor(tokens[1].val_number); + else + condition = std::floor(rt.resolve_var_num(tokens[1].val_string)); + + if(condition > 0.0f) + rt.jump_instructions(dest); + } + }, + { + .name = "jump", + .expected_num_args = 2, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + int offset, dest; + float condition; + + if(tokens[0].type == TokenType::LITERAL) + offset = std::floor(tokens[0].val_number); + else + offset = std::floor(rt.resolve_var_num(tokens[0].val_string)); + + if(tokens[1].type == TokenType::LITERAL) + condition = std::floor(tokens[1].val_number); + else + condition = std::floor(rt.resolve_var_num(tokens[1].val_string)); + + dest = rt.instruction_counter + dest; + + if(condition > 0.0f) + rt.jump_instructions(dest); + } + }, + { + .name = "strin", + .expected_num_args = 2, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::STRING, ValueType::ANY}, + .func = [](Runtime& rt, const std::vector& tokens) + { + if(tokens[0].type == TokenType::LITERAL) + std::cout << tokens[0].val_string << " "; + else + std::cout << rt.resolve_var_str(tokens[0].val_string) << " "; + + std::string str; + std::cin >> str; + + rt.assign_var_str(tokens[1].val_string, str); + } + }, + { + .name = "numin", + .expected_num_args = 2, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::STRING, ValueType::ANY}, + .func = [](Runtime& rt, const std::vector& tokens) + { + if(tokens[0].type == TokenType::LITERAL) + std::cout << tokens[0].val_string << " "; + else + std::cout << rt.resolve_var_str(tokens[0].val_string) << " "; + + float num; + std::cin >> num; + + rt.assign_var_num(tokens[1].val_string, num); + } + }, + { + .name = "rand", + .expected_num_args = 2, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::NUMBER, ValueType::ANY}, + .func = [](Runtime& rt, const std::vector& tokens) + { + int limit; + if(tokens[0].type == TokenType::LITERAL) + limit = tokens[0].val_number; + else + limit = rt.resolve_var_num(tokens[0].val_string); + + int number = rand() % limit; + + rt.assign_var_num(tokens[1].val_string, number); + } + }, + { + .name = "equal", + .expected_num_args = 3, + .expected_token_types = {TokenType::ID_OR_LIT, TokenType::ID_OR_LIT, TokenType::IDENTIFIER}, + .expected_value_types = {ValueType::NUMBER, ValueType::NUMBER, ValueType::NUMBER}, + .func = [](Runtime& rt, const std::vector& tokens) + { + float operands[2], res; + + // Retrieve operands + for(unsigned int i = 0; i < 2; i++) + { + if(tokens[i].type == TokenType::LITERAL) + operands[i] = tokens[i].val_number; + else + operands[i] = rt.resolve_var_num(tokens[i].val_string); + } + + res = std::round(operands[0]) == std::round(operands[1]); + + rt.assign_var_num(tokens[2].val_string, res); + } } }; diff --git a/src/keywords.hpp b/src/keywords.hpp index 9f3ee83..231a64f 100644 --- a/src/keywords.hpp +++ b/src/keywords.hpp @@ -4,8 +4,12 @@ #include #include #include +#include +#include "parse.hpp" #include "common.hpp" +#include "runtime.hpp" +#include "parse.hpp" struct Keyword { @@ -14,6 +18,8 @@ struct Keyword const std::vector expected_token_types; const std::vector expected_value_types; + + std::function&)> func; }; extern const std::vector keywords; diff --git a/src/main.cpp b/src/main.cpp index d70630f..b12b8cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "common.hpp" #include "exceptions.hpp" #include "parse.hpp" +#include "runtime.hpp" bool request_console(); @@ -46,19 +47,12 @@ std::ifstream get_infile(int argc, char ** argv) return infile; } -std::string code = \ -R"del( -PRINT 10.0 |hello_world -)del"; - int main(int argc, char * argv[]) { - /* // Get the entire source code from the file std::ifstream infile = get_infile(argc, argv); std::stringstream buffer; buffer << infile.rdbuf(); - */ // On Windows, we have to explicitly request a console /*if(!request_console()) @@ -67,13 +61,10 @@ int main(int argc, char * argv[]) exit(EXIT_FAILURE); }*/ - std::cerr << "POOP" << std::endl; + std::vector instructions = parse_instructions(buffer.str()); - std::vector instructions = parse_instructions(code); - - std::cerr << "Number of instructions parsed: " << instructions.size() << std::endl; - for(auto& instr : instructions) - instr.print(); + Runtime rt(instructions); + rt.run(); return 0; } diff --git a/src/parse.cpp b/src/parse.cpp index 1a0de21..7bfdf1f 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -20,18 +20,44 @@ std::vector tokenize_line(const std::string& line) std::size_t end, begin = line.find_first_not_of(blank_space, 0); while(begin != std::string::npos) { - end = line.find_first_of(blank_space, begin); - std::string word = line.substr(begin, end - begin); - tokens.emplace_back(word); + if(line[begin] == '"') // We have struck a string literal!!! + { + end = line.find("\"", begin + 1); - begin = line.find_first_not_of(blank_space, end); + if(end == std::string::npos) + { + std::cout << "ERROR: Unterminated string literal!" << std::endl; + std::exit(EXIT_FAILURE); + } + + end+=1; + std::string word = line.substr(begin, end - begin); + tokens.emplace_back(word); + + begin = line.find_first_not_of(blank_space, end); + } + else if(line[begin] == '#') // It's a comment.. discard rest of line! + { + break; + } + else + { + end = line.find_first_of(blank_space, begin); + std::string word = line.substr(begin, end - begin); + tokens.emplace_back(word); + + begin = line.find_first_not_of(blank_space, end); + } } return std::move(tokens); } +// Main parsing function std::vector parse_instructions(const std::string& code_arg) { + unsigned int line_number = 1; + // Make a copy of the code and append a line_end symbol to make sure the // parser doesn't break on EOF std::string code = code_arg; @@ -44,12 +70,51 @@ std::vector parse_instructions(const std::string& code_arg) { end = code.find_first_of(line_end, begin); std::string line = code.substr(begin, end - begin); - std::vector tokens = tokenize_line(line); - instructions.emplace_back(tokens); + + try + { + std::vector tokens = tokenize_line(line); + instructions.emplace_back(tokens); + } + catch(MalformedTokenExcept& e) + { + std::cout << "ERROR: In line " << line_number << ": " + << "Malformed identifier '" << e.malformed_str << "'" + << std::endl; + std::exit(EXIT_FAILURE); + } + catch(UnknownKeywordExcept& e) + { + std::cout << "ERROR: In line " << line_number << ": " + << "Unknown keyword '" << e.unknown_keyword << "'" + << std::endl; + std::exit(EXIT_FAILURE); + } + catch(WrongArgumentCountExcept& e) + { + std::cout << "ERROR: In line " << line_number << ": " + << "Wrong argument count. " << e.keyword_name + << " expects " << e.expected << " arguments but got " + << e.got << "." << std::endl; + std::exit(EXIT_FAILURE); + } + catch(WrongTokenExcept &e) + { + std::cout << "ERROR: In line " << line_number << ": " + << "Token '" << e.token_str << "' is wrong kind of token. Keyword '" + << e.keyword_name << "' expected " + << resolve_TokenType_str(e.expected) << " but got " + << resolve_TokenType_str(e.got) << "." << std::endl; + + std::exit(EXIT_FAILURE); + } begin = code.find_first_not_of(line_end, end); + + ++line_number; } + return std::move(instructions); } @@ -71,7 +136,7 @@ bool is_keyword(std::string str) bool is_string_literal(const std::string& str) { - if(str[0] == '#') + if(str[0] == '"' && str[str.size()-1] == '"') return true; else return false; @@ -88,8 +153,13 @@ bool is_decimal_literal(const std::string& str) bool is_identifier(const std::string& str) { + // First char must be alphabetic + if(!std::isalpha(str[0])) return false; + + // Rest of identifier must be alphanumeric, + // but can also contain underscores for(const char& c : str) - if(!(std::isalpha(c) || c == '_')) + if(!(std::isalnum(c)) && c != '_') return false; return true; } @@ -97,11 +167,15 @@ bool is_identifier(const std::string& str) bool Token::parse_as_keyword(const std::string &str) { - if(!is_keyword(str)) + auto str_cpy = str; + for(char &c : str_cpy) + c = (char) std::tolower(c); + + if(!is_keyword(str_cpy)) return false; type = TokenType::KEYWORD; - val_string = str; + val_string = str_cpy; return true; } @@ -112,17 +186,21 @@ bool Token::parse_as_literal(const std::string &str) if(is_string_literal(str)) { type = TokenType::LITERAL; - literal_type = ValueType::STRING; + value_type = ValueType::STRING; auto str_copy = str; + + // Erase the quotation marks str_copy.erase(0, 1); + str_copy.erase(str.size()-2, 1); + val_string = str_copy; return true; } else if(is_decimal_literal(str)) { type = TokenType::LITERAL; - literal_type = ValueType::NUMBER; - val_float = std::stof(str); + value_type = ValueType::NUMBER; + val_number = std::stof(str); val_string = str; return true; } @@ -137,9 +215,9 @@ bool Token::parse_as_identifier(const std::string &str) if(is_identifier(str)) { type = TokenType::IDENTIFIER; - literal_type = ValueType::ANY; + value_type = ValueType::ANY; val_string = str; - val_float = 0.0f; + val_number = 0.0f; return true; } return false; @@ -148,7 +226,7 @@ bool Token::parse_as_identifier(const std::string &str) Token::Token(const std::string &str) { // First try to parse as str, then as literal, then as identifier - if(!(parse_as_keyword(str) && parse_as_literal(str) && parse_as_identifier(str))) + if(!parse_as_keyword(str) && !parse_as_literal(str) && !parse_as_identifier(str)) throw MalformedTokenExcept(str); // if all fails -> malformed token } @@ -157,24 +235,27 @@ void Token::print() const switch(type) { case TokenType::IDENTIFIER: - std::cerr << "Identifier: \"" << val_string << "\""; + std::cout << "Identifier: " << val_string; break; case TokenType::KEYWORD: - std::cerr << "Keyword: \"" << val_string << "\""; + std::cout << "Keyword: \"" << val_string << "\""; break; case TokenType::LITERAL: - std::cerr << "Literal of type "; - if(literal_type == ValueType::NUMBER) - std::cerr << "decimal: " << val_float; - else if(literal_type == ValueType::STRING) - std::cerr << "string: \"" << val_string << "\""; + std::cout << "Literal of type "; + if(value_type == ValueType::NUMBER) + std::cerr << "Number: " << val_number; + else if(value_type == ValueType::STRING) + std::cout << "String: \"" << val_string << "\""; break; + + // Correctly parsed tokens should NEVER have this type: case TokenType::ID_OR_LIT: - std::cerr << "Identifier or Literal?! Something went terribly wrong!!"; + std::cout << "Something went wrong parsing this token!! " + << __FILE__ << " " << __LINE__; } - std::cerr << std::endl; + std::cout << std::endl; } Instruction::Instruction(std::vector& _token_list) @@ -203,9 +284,21 @@ Instruction::Instruction(std::vector& _token_list) // Check the arguments supplied are the right kind of tokens for(int i = 0; i < keyword_ptr->expected_num_args; i++) { - if(keyword_ptr->expected_token_types[i] != tokens[i+1].type) - throw WrongTokenExcept(keyword_ptr->name, tokens[i+1].val_string, keyword_ptr->expected_token_types[i], - tokens[i+1].type); + switch(keyword_ptr->expected_token_types[i]) + { + case TokenType::ID_OR_LIT: + if(tokens[i+1].type != TokenType::IDENTIFIER && tokens[i+1].type != TokenType::LITERAL) + throw WrongTokenExcept(keyword_ptr->name, tokens[i+1].val_string, + keyword_ptr->expected_token_types[i], + tokens[i+1].type); + break; + default: + if(tokens[i+1].type != keyword_ptr->expected_token_types[i]) + throw WrongTokenExcept(keyword_ptr->name, tokens[i+1].val_string, + keyword_ptr->expected_token_types[i], + tokens[i+1].type); + break; + } } // if arbitrary number of args are expected, they're all one type @@ -213,9 +306,21 @@ Instruction::Instruction(std::vector& _token_list) { for (auto it = tokens.begin() + 1; it != tokens.end(); it++) { - if (keyword_ptr->expected_token_types[0] != it->type) - throw WrongTokenExcept(keyword_ptr->name, it->val_string, keyword_ptr->expected_token_types[0], - it->type); + switch(keyword_ptr->expected_token_types[0]) + { + case TokenType::ID_OR_LIT: + if(it->type != TokenType::IDENTIFIER && it->type != TokenType::LITERAL) + throw WrongTokenExcept(keyword_ptr->name, it->val_string, + keyword_ptr->expected_token_types[0], + it->type); + break; + default: + if(it->type != keyword_ptr->expected_token_types[0]) + throw WrongTokenExcept(keyword_ptr->name, it->val_string, + keyword_ptr->expected_token_types[0], + it->type); + break; + } } } diff --git a/src/parse.hpp b/src/parse.hpp index 82613d4..e100585 100644 --- a/src/parse.hpp +++ b/src/parse.hpp @@ -6,6 +6,7 @@ #include "keywords.hpp" #include "common.hpp" +#include "runtime.hpp" struct Token { @@ -19,8 +20,8 @@ private: public: enum TokenType type; - enum ValueType literal_type; - float val_float; + enum ValueType value_type; + float val_number; std::string val_string; Token(const std::string& str); @@ -28,12 +29,11 @@ public: void print() const; }; -class Instruction +struct Instruction { std::vector tokens; const Keyword * keyword_ptr; -public: // Try to parse a list of symbols as instructions Instruction(std::vector& _token_list); void print(); diff --git a/src/runtime.cpp b/src/runtime.cpp new file mode 100644 index 0000000..afa1024 --- /dev/null +++ b/src/runtime.cpp @@ -0,0 +1,150 @@ +#include +#include + +#include "runtime.hpp" +#include "exceptions.hpp" + +bool Runtime::var_exists(const std::string& identifier) +{ + return std::any_of(variables.begin(), variables.end(), + [&identifier](auto& var) { + return var.name == identifier; + }); +} + +const std::vector::iterator Runtime::var_find(const std::string& identifier) +{ + auto it = std::find_if(variables.begin(), variables.end(), + [&identifier](auto& var) { + return var.name == identifier; + }); + + if(it == variables.end()) + throw UndeclaredVariableExcept(identifier); + + return it; +} + +ValueType Runtime::resolve_var_type(const std::string& identifier) +{ + for(auto& var : variables) + { + if(var.name == identifier) + return var.type; + } + + throw UndeclaredVariableExcept(identifier); +} + +const std::string Runtime::resolve_var_str(const std::string& identifier) +{ + if(resolve_var_type(identifier) != ValueType::STRING) + throw TypeErrorExcept(identifier, ValueType::STRING, ValueType::NUMBER); + + return var_find(identifier)->val_string; +} + +float Runtime::resolve_var_num(const std::string& identifier) +{ + if(resolve_var_type(identifier) != ValueType::NUMBER) + throw TypeErrorExcept(identifier, ValueType::NUMBER, ValueType::STRING); + + return var_find(identifier)->val_number; +} + +void Runtime::assign_var_num(const std::string& identifier, float num) +{ + // Because redeclaration of variables is allowed + if(var_exists(identifier)) + { + auto it = std::find_if(variables.begin(), variables.end(), + [&identifier](auto& var) { + return identifier == var.name; + }); + + it->type = ValueType::NUMBER; + it->val_number = num; + it->val_string = ""; + } + else + variables.emplace_back(identifier, ValueType::NUMBER, "", num); +} + +void Runtime::assign_var_str(const std::string& identifier, const std::string& str) +{ + // Because redeclaration of variables is allowed + if(var_exists(identifier)) + { + auto it = std::find_if(variables.begin(), variables.end(), + [&identifier](auto& var) { + return identifier == var.name; + }); + + it->type = ValueType::NUMBER; + it->val_number = 0; + it->val_string = str; + } + else + variables.emplace_back(identifier, ValueType::STRING, str, 0); +} + +void Runtime::execute_instruction(const Instruction& instruction) +{ + instruction.keyword_ptr->func(*this, instruction.tokens); +} + +void Runtime::jump_instructions(int new_instr_counter) +{ + if(new_instr_counter > instructions.size() || new_instr_counter < 1) + throw JumpOutOfBoundsExcept(instruction_counter + 1, new_instr_counter); + else + instruction_counter = new_instr_counter - 2; // -1 to account for increment after each instr +} + +int Runtime::run() +{ + // reset everything + instruction_counter = 0; + variables.clear(); + + for(;;) + { + if(instruction_counter >= instructions.size()) + { + std::cout << "ERROR: Unexpected end of instructions!" << std::endl; + std::exit(EXIT_FAILURE); + } + try // Execute our instruction + { + execute_instruction(instructions[instruction_counter]); + } + catch(TypeErrorExcept& e) + { + std::cout << "ERROR: In line " << instruction_counter + 1 << ": " + << "Type mismatch. Argument '" << e.token_str + << "' has type " << resolve_ValueType_str(e.got) + << " but " << resolve_ValueType_str(e.expected) + << " was expected." << std::endl; + std::exit(EXIT_FAILURE); + } + catch(UndeclaredVariableExcept& e) + { + std::cout << "ERROR: In line " << instruction_counter + 1 << ": " + << "Undefined variable '" << e.identifier_str + << "'" << std::endl; + std::exit(EXIT_FAILURE); + } + catch(JumpOutOfBoundsExcept& e) + { + std::cout << "ERROR: In line " << e.from << ": " + << "Jump target '" << e.to << "' is out of bounds." + << std::endl; + std::exit(EXIT_FAILURE); + } + + // Increment instruction counter + ++instruction_counter; + } + + return 0; +} diff --git a/src/runtime.hpp b/src/runtime.hpp new file mode 100644 index 0000000..b9a385e --- /dev/null +++ b/src/runtime.hpp @@ -0,0 +1,45 @@ +#ifndef BISCUIT_RUNTIME_HPP +#define BISCUIT_RUNTIME_HPP + +#include +#include "common.hpp" +#include "parse.hpp" + +struct Variable +{ + std::string name; + ValueType type; + + std::string val_string; + float val_number; + + Variable(const std::string& _name, ValueType _type, const std::string& _string, float _number) + : name(_name), type(_type), val_string(_string), val_number(_number) {} +}; + +struct Runtime +{ + int instruction_counter; + std::vector instructions; + std::vector variables; + + inline Runtime(const std::vector& _instructions) + : instruction_counter(1), instructions{_instructions} {} + + const std::vector::iterator var_find(const std::string& identifier); + bool var_exists(const std::string& identifier); + ValueType resolve_var_type(const std::string& identifier); + const std::string resolve_var_str(const std::string& identifier); + float resolve_var_num(const std::string& identifier); + + void assign_var_num(const std::string& identifier, float num); + void assign_var_str(const std::string& identifier, const std::string& str); + + void jump_instructions(int new_instr_counter); + + void execute_instruction(const Instruction& instruction); + + int run(); +}; + +#endif diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..24d4106 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,41 @@ +#include "common.hpp" +#include + +const std::string resolve_TokenType_str(enum TokenType ttype) +{ + switch(ttype) + { + case TokenType::KEYWORD: + return "Keyword"; + break; + case TokenType::IDENTIFIER: + return "Identifier"; + break; + case TokenType::LITERAL: + return "Literal"; + break; + case TokenType::ID_OR_LIT: + return "Identifer or Literal"; + break; + } + + return "UNKNOW TOKENTYPE"; +} + +const std::string resolve_ValueType_str(enum ValueType vtype) +{ + switch(vtype) + { + case ValueType::STRING: + return "String"; + break; + case ValueType::NUMBER: + return "Number"; + break; + case ValueType::ANY: + return "Any"; + break; + } + + return "UNKNOWN VALUETYPE"; +}