Created
October 27, 2018 20:29
-
-
Save itarato/d7270c96a3ee5332d397fc92a71be533 to your computer and use it in GitHub Desktop.
Stack base interpreter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <utility> | |
#include <vector> | |
#include <regex> | |
#include <unordered_map> | |
#include <cmath> | |
using namespace std; | |
using value_t = double; | |
template<typename T> | |
void debug_vec(vector<T> v) { | |
size_t i{0}; | |
for (const auto & e : v) { | |
cout << '#' << i++ << ": '" << e << "'" << endl; | |
} | |
} | |
enum IfState { | |
Exec, | |
Wait, | |
}; | |
namespace StackLang { | |
value_t add(value_t lhs, value_t rhs) { return lhs + rhs; } | |
value_t mul(value_t lhs, value_t rhs) { return lhs * rhs; } | |
value_t div(value_t lhs, value_t rhs) { return lhs / rhs; } | |
value_t sub(value_t lhs, value_t rhs) { return lhs - rhs; } | |
value_t pow(value_t lhs, value_t rhs) { return std::pow(lhs, rhs); } | |
value_t sqrt(value_t v) { return std::sqrt(v); } | |
value_t print(value_t lhs) { | |
cout << lhs << endl; | |
return lhs; | |
} | |
} | |
struct Tokenizer { | |
string source; | |
Tokenizer(string &&source) : source(move(source)) { }; | |
vector<string> tokenize() { | |
vector<string> out{}; | |
size_t prev_find{0}; | |
while (prev_find < source.size()) { | |
size_t current_find = source.find(' ', prev_find); | |
if (current_find == string::npos) { | |
current_find = source.size(); | |
} | |
out.emplace_back(source.substr(prev_find, current_find - prev_find)); | |
prev_find = current_find + 1; | |
} | |
return out; | |
}; | |
}; | |
struct Interpreter { | |
Tokenizer tokenizer; | |
vector<value_t> stack; | |
vector<IfState> if_states; | |
unordered_map<string, vector<string>> functions; | |
Interpreter(string &&source) : tokenizer({move(source)}), if_states({}) { | |
tokens = tokenizer.tokenize(); | |
tokens_it = tokens.begin(); | |
}; | |
void interpret() { | |
const regex num_regex("^[\\-+]?\\d+(\\.\\d+|)$"); | |
for (optional<string> token_opt = next_token(); token_opt.has_value(); token_opt = next_token()) { | |
const string token = token_opt.value(); | |
cout << "Token read: '" << token << '\'' << endl; | |
if (token == "if") { | |
if (top() != 0.0) { | |
if_states.emplace_back(IfState::Exec); | |
} else { | |
if_states.emplace_back(IfState::Wait); | |
} | |
continue; | |
} else if (token == "else") { | |
if (if_states.back() == IfState::Exec) { | |
if_states.back() = IfState::Wait; | |
} else { | |
if_states.back() = IfState::Exec; | |
} | |
continue; | |
} else if (token == "endif") { | |
if_states.pop_back(); | |
continue; | |
} else if (!if_states.empty() && if_states.back() == IfState::Wait) { | |
continue; | |
} | |
if (regex_match(token, num_regex)) { | |
push(stod(token)); | |
} else if (token == "fn") { | |
read_function(); | |
} else if (token == "+") { | |
push(StackLang::add(pop(), pop())); | |
} else if (token == "print") { | |
push(StackLang::print(pop())); | |
} else if (token == "flush") { | |
flush(); | |
} else if (token == "dup") { | |
dup(); | |
} else if (token == "nop") { | |
// No op. | |
} else if (token == "pop") { | |
pop(); | |
} else { | |
auto fn_it = functions.find(token); | |
if (fn_it != functions.end()) { | |
current_fn_key = token; | |
current_fn_ip = 0; | |
} else { | |
cerr << "Unknown command." << endl; | |
abort(); | |
} | |
} | |
} | |
}; | |
private: | |
vector<string> tokens; | |
vector<string>::iterator tokens_it; | |
optional<string> current_fn_key{}; | |
size_t current_fn_ip{0}; | |
value_t pop() { | |
if (stack.empty()) { | |
cerr << "Empty stack cannot pop." << endl; | |
abort(); | |
} | |
value_t val{stack.back()}; | |
stack.pop_back(); | |
cout << "POP <" << val << "> to stack." << endl; | |
return val; | |
}; | |
void push(value_t val) { | |
stack.emplace_back(val); | |
cout << "PUSH <" << val << "> to stack." << endl; | |
}; | |
value_t top() { | |
if (stack.empty()) { | |
cerr << "Empty stack has no top." << endl; | |
abort(); | |
} | |
return stack.back(); | |
} | |
void flush() { | |
stack.clear(); | |
cout << "FLUSH" << endl; | |
}; | |
void dup() { | |
push(stack.back()); | |
}; | |
optional<string> next_token() { | |
if (current_fn_key.has_value()) { | |
string token = functions[current_fn_key.value()][current_fn_ip]; | |
current_fn_ip++; | |
if (current_fn_ip >= functions[current_fn_key.value()].size()) { | |
current_fn_key = {}; | |
} | |
return {token}; | |
} | |
if (tokens_it != tokens.end()) { | |
string token = *tokens_it; | |
tokens_it++; | |
return {token}; | |
} | |
return {}; | |
} | |
void read_function() { | |
string name = next_token().value(); | |
vector<string> tokens{}; | |
for (optional<string> token_opt = next_token(); token_opt.has_value() && token_opt.value() != "endfn"; token_opt = next_token()) { | |
tokens.emplace_back(token_opt.value()); | |
} | |
functions[name] = tokens; | |
} | |
}; | |
int main() { | |
Interpreter("1 print").interpret(); | |
Interpreter("fn add + print endfn 2 5 add").interpret(); | |
Interpreter("4 if 1 print else 2 print if 3 print else 4 print endif endif ").interpret(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment