version 0.1

This commit is contained in:
Hans 2021-11-17 19:19:49 +01:00
parent eddfeb7bfc
commit dbfdde72ab
20 changed files with 740 additions and 55 deletions

View file

@ -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})

Binary file not shown.

BIN
doc/biscuit_doc.odt Normal file

Binary file not shown.

BIN
doc/biscuit_doc.pdf Normal file

Binary file not shown.

9
examples/faculty.bisc Normal file
View file

@ -0,0 +1,9 @@
PRINT "This program calculates the faculty of a number."
ASSIGN 1 fac
NUMIN "Please input your number!" num
ASSIGN num i
MUL fac i fac
SUB i 1 i
GOTO 5 i
PRINT "The faculty of " num " is " fac "."
EXIT

11
examples/fibonacci.bisc Normal file
View file

@ -0,0 +1,11 @@
ASSIGN 8 n
ASSIGN 0 a
ASSIGN 1 b
PRINT a
ADD a b a
PRINT a
ADD a b b
PRINT b
SUB n 1 n
GOTO 5 n
EXIT

View file

@ -0,0 +1,13 @@
PRINT "Welcome to the number guessing game!"
RAND 100 number
NUMIN "Please input your guess!" guess
EQUAL number guess is_equal
GOTO 12 is_equal # win if equal
SUB number guess is_less
GOTO 10 is_less
PRINT "Too big! Try again!"
GOTO 3 1
PRINT "Too small! Try again!"
GOTO 3 1
PRINT "Correct! The number was " number "."
EXIT

View file

@ -1,2 +1,2 @@
print #hello_world
print 1.01234
PRINT "Hello world!" # This is a comment
EXIT

5
examples/numbers.bisc Normal file
View file

@ -0,0 +1,5 @@
ASSIGN 10 i
PRINT i
SUB i 1 i
GOTO 2 i
EXIT

11
examples/test.bisc Normal file
View file

@ -0,0 +1,11 @@
PRINT "Hello World, this is a number: " 15
ASSIGN number 15
PRINT "Let's double it's value, shall we?"
ADD number number number
PRINT "There we go: " number
PRINT "Now, what if we multiplied it by 5?"
MUL number 5 number
PRINT number
PRINT "Not bad :)"
PRINT "The program will now terminate!"
EXIT

View file

@ -1,6 +1,8 @@
#ifndef BISCUIT_INTERPRETER_COMMON_HPP
#define BISCUIT_INTERPRETER_COMMON_HPP
#include <string>
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

View file

@ -4,6 +4,7 @@
#include <exception>
#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

View file

@ -1,5 +1,7 @@
#include <string>
#include <functional>
#include <iostream>
#include <cmath>
#include "common.hpp"
#include "keywords.hpp"
@ -10,10 +12,277 @@ const std::vector<Keyword> 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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<Token>& 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);
}
}
};

View file

@ -4,8 +4,12 @@
#include <string>
#include <vector>
#include <array>
#include <functional>
#include "parse.hpp"
#include "common.hpp"
#include "runtime.hpp"
#include "parse.hpp"
struct Keyword
{
@ -14,6 +18,8 @@ struct Keyword
const std::vector<enum TokenType> expected_token_types;
const std::vector<enum ValueType> expected_value_types;
std::function<void (Runtime&, const std::vector<Token>&)> func;
};
extern const std::vector<Keyword> keywords;

View file

@ -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<Instruction> instructions = parse_instructions(buffer.str());
std::vector<Instruction> 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;
}

View file

@ -20,18 +20,44 @@ std::vector<Token> 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<Instruction> 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<Instruction> 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<Token> tokens = tokenize_line(line);
instructions.emplace_back(tokens);
try
{
std::vector<Token> 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>& _token_list)
@ -203,9 +284,21 @@ Instruction::Instruction(std::vector<Token>& _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>& _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;
}
}
}

View file

@ -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<Token> tokens;
const Keyword * keyword_ptr;
public:
// Try to parse a list of symbols as instructions
Instruction(std::vector<Token>& _token_list);
void print();

150
src/runtime.cpp Normal file
View file

@ -0,0 +1,150 @@
#include <iostream>
#include <cmath>
#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<Variable>::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;
}

45
src/runtime.hpp Normal file
View file

@ -0,0 +1,45 @@
#ifndef BISCUIT_RUNTIME_HPP
#define BISCUIT_RUNTIME_HPP
#include <string>
#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<Instruction> instructions;
std::vector<Variable> variables;
inline Runtime(const std::vector<Instruction>& _instructions)
: instruction_counter(1), instructions{_instructions} {}
const std::vector<Variable>::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

41
src/util.cpp Normal file
View file

@ -0,0 +1,41 @@
#include "common.hpp"
#include <string>
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";
}