Skip to content

Instantly share code, notes, and snippets.

@mtvee
Last active July 26, 2020 15:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mtvee/63e0da816e343f6c3a20f514c600f27d to your computer and use it in GitHub Desktop.
Save mtvee/63e0da816e343f6c3a20f514c600f27d to your computer and use it in GitHub Desktop.
A puny forth in c++ for no good reason
/*
* PunyForth
*
* Copyright 2020(c) mtvee
* All rights reserved.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
* to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/*
* Morning coffee Forth.
*
* So there I was, slurping my morning coffee, bored to death by covid lockdown and perusing
* microforth (https://github.com/Earlz/microforth) code and thought to myself, "This is neat
* but it needs to be in c++."
*
* You won't believe what happened next...
*
*/
#include <string>
#include <functional>
#include <map>
#include <stack>
#include <sstream>
#include <iterator>
#include <iostream>
typedef intptr_t cell;
typedef std::function<void()> mf_word_func;
typedef struct mf_word {
std::string name;
std::string body;
mf_word_func func;
} mf_word_t;
class Forth
{
public:
Forth();
void exec_word(const std::string &word);
bool exec(const std::string &code);
void add_word(const std::string &word, const std::string &body);
void add_word(const std::string &word, mf_word_func fn);
void push(cell value);
cell pop();
cell top() { return m_stack.top(); }
std::string err_message() { return error_msg; }
private:
std::stack<cell> m_stack;
int error;
std::string error_msg;
std::map<std::string, mf_word> m_words;
};
Forth::Forth()
: m_stack()
, m_words()
, error(0)
{}
void Forth::push(cell value)
{
m_stack.push(value);
}
cell Forth::pop()
{
auto val = m_stack.top();
m_stack.pop();
return val;
}
void Forth::exec_word(const std::string &word)
{
try {
auto v = std::stol(word);
push(v);
}
catch( std::invalid_argument & ) {
if(m_words.count(word) > 0) {
auto w = m_words[word];
if(w.func) {
w.func();
}
else if (!w.body.empty()){
exec(w.body);
}
else {
error = 1;
error_msg = "Word has nothing to do :( " + word;
}
}
else {
error = 1;
error_msg = "Unknown word: " + word;
}
}
}
bool Forth::exec(const std::string &code)
{
std::istringstream sstr(code);
std::vector<std::string> words(std::istream_iterator<std::string>{sstr},
std::istream_iterator<std::string>());
bool creating_word = false;
mf_word new_word;
for(auto &w: words) {
if(w == ":") {
creating_word = true;
continue;
}
if(w == ";") {
creating_word = false;
add_word(new_word.name, new_word.body);
new_word.name = "";
new_word.body = "";
continue;
}
if(!creating_word) {
exec_word(w);
if (error != 0) {
return false;
}
}
else {
if( new_word.name.empty()) {
new_word.name = w;
}
else {
new_word.body += w + " ";
}
}
}
return true;
}
void Forth::add_word(const std::string &word, mf_word_func fn)
{
m_words[word] = {word, "", std::move(fn)};
}
void Forth::add_word(const std::string &word, const std::string &body)
{
m_words[word] = {word, body, nullptr};
}
int main(int argc, char **argv)
{
Forth f;
// print out the stack top
f.add_word(".", [&]() {
std::cout << f.top() << std::endl;
});
// add two elements on the stack
f.add_word("+", [&]() {
f.push(f.pop() + f.pop());
});
// make a word to add
f.add_word("add", "+");
// run tings
if(!f.exec("1 1 + .")) {
std::cerr << f.err_message() << std::endl;
return 1;
}
if(!f.exec("1 1 add .")) {
std::cerr << f.err_message() << std::endl;
return 1;
}
// create w a word to add 1
if(!f.exec(": add1 1 + ;")) {
std::cerr << f.err_message() << std::endl;
return 1;
}
if(!f.exec("2 add1 .")) {
std::cerr << f.err_message() << std::endl;
return 1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment