/* Ground Copyright (C) 2025 Maxwell Jeffress This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* With the licence out of the way, let's begin! Ground is a programming language which takes some inspiration from Assembly in it's design, but has higher level features (more types, simpler IO, easier variables, etc) which make it easy to use. Ground works even better if you write a programming language that compiles to Ground code. Ground is designed to have a similar purpose to the Java Virtual Machine, except easier for anyone to write their own highly opinionated language on top of. See documentation for building, maintaining and writing Ground code in the docs folder in the repo. Happy coding! */ #include #include #include #include #include #include using namespace std; /* Instructions enum class For each function keyword, an instruction is assigned. See also parser function, interpreter function, Instruction struct */ enum class Instructions { Jump, If, Stdout, Stdin, Stdlnout, Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, End, Set, Empty, Setlist, Getlistat, Setlistat, Getlistsize }; /* Types enum class Assists in type checking in the parser function. For example, the following values correspond to the following types: 1 Int 3.14 Double "Hello!" String 'e' Char true Bool $value Value &var Direct %10 Line See also parser function */ enum class Types { Int, Double, String, Char, Bool, Value, Direct, Line, ListRef }; /* Literal struct Contains literal values. For example, if the following line was written: stdout "Hello world!" The Literal struct in the instruction should look like this: { val = "Hello world!"; // I am ignoring the variant for simplicity // of documenting the code } All value references are swapped out for their respective Literal they point to. See also variables map, parser function, interpreter function */ struct Literal { variant val; }; /* List struct Contains literal values inside a vector. For example, if the following program was written: setlist #myNums 3 5 9 13 The List struct which would be created and stored should look like this: { val = { Literal { val = 3 }, Literal { val = 5 }, Literal { val = 9 }, Literal { val = 13 }, } } All elements in the list must be of the same type. See also Literal struct. */ struct List { vector val; }; /* ListRef struct Contains the name of a list referenced by the program. For example, if the following program was written: setlist #myNums 3 5 9 13 The ListRef struct should look like this: { listName = "myNums"; } */ struct ListRef { string listName; }; /* variables map Contains all variables made while running the program. See also Literal struct. */ map variables; /* lists map Contains all lists made while running the program. See also List struct. */ map lists; /* ValueRef struct If the program being executed makes a value reference, it is stored in a ValueRef struct. For example, if the following line was written: stdin &myVar The ValueRef struct in the instruction should look like this: { varName = "myVar"; } */ struct ValueRef { string varName; }; /* Direct struct If the program being executed makes a direct reference, it is stored in a Direct struct. For example, if the following line was written: stdin &myVar The Direct struct in the instruction should look like this: { varName = "myVar"; } */ struct Direct { string varName; }; /* Line struct If the program being executed makes a line reference, it is stored in a Line struct. For example, if the following line was written: jump %10 The Line struct in the instruction should look like this: { lineNum = 10; } */ struct Line { int lineNum; }; /* Instruction struct An instruction usually corresponds to a line in the program being interpreted. For example, if the following line was written: add 5 $myVar &outVar The instruction should look like this: { inst = Instructions::Add; args = { Literal { val = 5 }, ValueRef { varName = "myVar" }, Direct { varName = "outVar" } }; } inst starts as empty, so empty lines and commented out lines do not get in the way of jump and if. See also: Instructions enum class, Literal struct, ValueRef struct, Direct struct, Line struct, exec function, parser function */ struct Instruction { Instructions inst = Instructions::Empty; vector> args; }; /* error function Takes a string (which is a debug message) and prints it to the console, letting the user know what went wrong with the program. */ void error(string in, int exitc = 1) { cout << "\033[31mError: \033[39m" << in << endl; exit(exitc); } /* is* functions These functions determine if a string value can be converted into a different type. */ bool isInt(string in) { try { stoi(in); if (stod(in) != stoi(in)) return false; return true; } catch (...) { return false; } } bool isDouble(string in) { try { stod(in); return true; } catch (...) { return false; } } bool isBool(string in) { if (in == "true" || in == "false") return true; else return false; } bool isString(string in) { if (in.size() >= 2 && in[0] == '"' && in[in.size() - 1] == '"') return true; else return false; } bool isChar(string in) { if (in.size() == 3 && in[0] == '\'' && in[in.size() - 1] == '\'') return true; else return false; } bool isValue(string in) { if (in.size() >= 1 && in[0] == '$') return true; else return false; } bool isDirect(string in) { if (in.size() >= 1 && in[0] == '&') return true; else return false; } bool isLine(string in) { if (in.size() >= 1 && in[0] == '%') return true; else return false; } bool isListRef(string in) { if (in.size() >= 1 && in[0] == '*') return true; else return false; } /* getType function This function determines the type of a value inside a string based on the is* functions above. Returns a type from the Types enum class. */ Types getType(string in) { if (isInt(in)) return Types::Int; if (isDouble(in)) return Types::Double; if (isBool(in)) return Types::Bool; if (isString(in)) return Types::String; if (isChar(in)) return Types::Char; if (isValue(in)) return Types::Value; if (isDirect(in)) return Types::Direct; if (isLine(in)) return Types::Line; if (isListRef(in)) return Types::ListRef; error("Could not determine type of \"" + in + "\""); return Types::Int; } /* exec function This function takes a list of instructions (see Instruction struct above and parser function below) and acts upon their properties. This is the main interpreter function for the program. */ void exec(vector in) { for (int i = 0; i < in.size(); i++) { Instruction l = in[i]; // Pre process value references for (int j = 0; j < l.args.size(); j++) { if (holds_alternative(l.args[j])) { if (variables.find(get(l.args[j]).varName) != variables.end()) { l.args[j] = variables[get(l.args[j]).varName]; } else { error("Could not find variable " + get(l.args[j]).varName); } } } switch (l.inst) { // Ignore empty lines case Instructions::Empty: break; /* stdout instruction This instruction prints characters to the console. It obtains the value from the variant and prints it. */ case Instructions::Stdout: if (l.args.size() < 1) { error("Could not find argument for Stdout inbuilt"); } if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val); } else if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val); } else if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val); } else if (holds_alternative(get(l.args[0]).val)) { if (get(get(l.args[0]).val) == true) { cout << "true"; } else { cout << "false"; } } else if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val); } else { error("Couldn't print that"); } } else { error("Argument of stdlnout must be a value (literal or a value reference)"); } break; /* stdlnout instruction This instruction prints characters to the console. It obtains the value from the variant and prints it, with a new line at the end. */ case Instructions::Stdlnout: if (l.args.size() < 1) { error("Could not find argument for Stdout inbuilt"); } if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val) << endl; } else if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val) << endl; } else if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val) << endl; } else if (holds_alternative(get(l.args[0]).val)) { if (get(get(l.args[0]).val) == true) { cout << "true" << endl; } else { cout << "false" << endl; } } else if (holds_alternative(get(l.args[0]).val)) { cout << get(get(l.args[0]).val) << endl; } else { error("Couldn't print that"); } } else { error("Argument of stdlnout must be a value (literal or a value reference)"); } break; /* set instruction This instruction sets a variable to a provided value. */ case Instructions::Set: if (l.args.size() < 2) { error("Could not find all arguments required for Set inbuilt"); } { string varName; Literal varContents; if (holds_alternative(l.args[0])) { varName = get(l.args[0]).varName; } else { error("First argument of set must be a direct reference"); } if (holds_alternative(l.args[1])) { varContents = get(l.args[1]); } else { error("Second argument of set must be a value (literal or value reference)"); } variables[varName] = varContents; } break; /* setlist instruction This instruction takes a potentially infinite amount of arguments and saves them to a list (vector) with name listName. */ case Instructions::Setlist: if (l.args.size() < 2) { error("Could not find all arguments required for Setlist inbuilt"); } { string listName; List listContents; if (holds_alternative(l.args[0])) { listName = get(l.args[0]).listName; } else { error("First argument of setlist must be a list reference"); } bool first = true; for (variant k : l.args) { if (holds_alternative(k)) { listContents.val.push_back(get(k)); } else { if (!first) error("All arguments after first in setlist must be values"); first = false; } } lists[listName] = listContents; } break; /* getlistat instruction This instruction gets a list item from a list and an index and saves the value to a variable. */ case Instructions::Getlistat: if (l.args.size() < 3) { error("Could not find all arguments required for Getlistat inbuilt"); } { ListRef listref; int ref; Direct var; if (holds_alternative(l.args[0])) { listref = get(l.args[0]); } else { error("First argument of getlistat must be a list reference"); } if (holds_alternative(l.args[1])) { if (holds_alternative(get(l.args[1]).val)) { ref = get(get(l.args[1]).val); } else { error("Second argument of getlistat must be an integer literal"); } } else { error("Second argument of getlistat must be an integer literal"); } if (holds_alternative(l.args[2])) { var = get(l.args[2]); } else { error("Third argument of getlistat must be a direct reference"); } if (lists.find(listref.listName) != lists.end()) { if (lists[listref.listName].val.size() > ref) { variables[var.varName] = lists[listref.listName].val[ref]; } else { error("Index " + to_string(ref) + " out of range of list " + listref.listName); } } else { error("Unknown list: " + listref.listName); } } break; case Instructions::Getlistsize: if (l.args.size() < 2) { error("Could not find all arguments required for Getlistsize inbuilt"); } { ListRef ref; Direct var; if (holds_alternative(l.args[0])) { ref = get(l.args[0]); } else { error("First argument of getlistsize must be a list reference"); } if (holds_alternative(l.args[1])) { var = get(l.args[1]); } else { error("Second argument of getlistsize must be a direct reference"); } Literal newLit; if (lists.find(ref.listName) != lists.end()) { newLit.val = int(lists[ref.listName].val.size()); variables[var.varName] = newLit; } else { error("Couldn't find the list " + ref.listName); } break; } /* stdin instruction This instruction takes input from the standard character input via the C++ getline() function, and saves it to a variable. */ case Instructions::Stdin: if (l.args.size() < 1) { error("Could not find all arguments required for Stdin inbuilt"); } if (holds_alternative(l.args[0])) { string userIn; getline(cin, userIn); Literal userLit; userLit.val = userIn; variables[get(l.args[0]).varName] = userLit; } else { error("First argument of stdin must be a direct reference"); } break; /* add instruction This instruction adds two values (can be numbers or strings) and outputs it to the direct reference given. */ case Instructions::Add: if (l.args.size() < 3) { error("Could not find all arguments required for Add inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of add must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of add must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of add must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) + get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) + get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) + get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) + double(get(right.val)); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) + get(right.val); } else { error("Cannot add those two values"); } variables[varName] = final; } break; /* subtract instruction This instruction subtracts two values and outputs it to the direct reference given. */ case Instructions::Subtract: if (l.args.size() < 3) { error("Could not find all arguments required for Subtract inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of subtract must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of subtract must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of subtract must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) - get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) - get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) - get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) - double(get(right.val)); } else { error("Cannot subtract those two values"); } variables[varName] = final; } break; /* multiply instruction This instruction multiplies two numbers and outputs it to the direct reference given. */ case Instructions::Multiply: if (l.args.size() < 3) { error("Could not find all arguments required for Multiply inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of multiply must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of multiply must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of multiply must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) * get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) * get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) * get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) * double(get(right.val)); } else { error("Cannot multiply those two values"); } variables[varName] = final; } break; /* divide instruction This instruction divides two numbers and outputs it to the direct reference given. */ case Instructions::Divide: if (l.args.size() < 3) { error("Could not find all arguments required for Divide inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of divide must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of divide must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of divide must be a direct reference"); } // Ensure we aren't dividing by zero if (holds_alternative(right.val)) { if (get(right.val) == 0) { error("Division by zero is not allowed"); } } if (holds_alternative(right.val)) { if (get(right.val) == 0) { error("Division by zero is not allowed"); } } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) / get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) / get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) / get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) / double(get(right.val)); } else { error("Cannot divide those two values"); } variables[varName] = final; } break; /* equal instruction This instruction checks if two values are equal and outputs a boolean to the direct reference given. */ case Instructions::Equal: if (l.args.size() < 3) { error("Could not find all arguments required for Equal inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of equal must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of equal must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of equal must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) == get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) == get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) == get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) == double(get(right.val)); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) == get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) == get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) == get(right.val); } else { error("Cannot equal those two values"); } variables[varName] = final; } break; /* inequal instruction This instruction checks if two values are not equal and outputs a boolean to the direct reference given. */ case Instructions::Inequal: if (l.args.size() < 3) { error("Could not find all arguments required for Inequal inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of inequal must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of inequal must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of inequal must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) != get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) != get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) != get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) != double(get(right.val)); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) != get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) != get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) != get(right.val); } else { error("Cannot inequal those two values"); } variables[varName] = final; } break; /* greater instruction This instruction checks if the left value is greater than the right value and outputs a boolean to the direct reference given. */ case Instructions::Greater: if (l.args.size() < 3) { error("Could not find all arguments required for Greater inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of greater must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of greater must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of greater must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) > get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) > get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) > get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) > double(get(right.val)); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) > get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) > get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) > get(right.val); } else { error("Cannot greater those two values"); } variables[varName] = final; } break; /* lesser instruction This instruction checks if the left value is lesser than the right value and outputs a boolean to the direct reference given. */ case Instructions::Lesser: if (l.args.size() < 3) { error("Could not find all arguments required for Lesser inbuilt"); } { string varName; Literal left; Literal right; Literal final; if (holds_alternative(l.args[0])) { left = get(l.args[0]); } else { error("First argument of lesser must be a value (literal or value reference)"); } if (holds_alternative(l.args[1])) { right = get(l.args[1]); } else { error("Second argument of lesser must be a value (literal or value reference)"); } if (holds_alternative(l.args[2])) { varName = get(l.args[2]).varName; } else { error("Third argument of lesser must be a direct reference"); } // Figure out types and compute values if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) < get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) < get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = double(get(left.val)) < get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) < double(get(right.val)); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) < get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) < get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val) < get(right.val); } else { error("Cannot lesser those two values"); } variables[varName] = final; } break; /* jump instruction Jumps to the line reference provided. */ case Instructions::Jump: if (l.args.size() < 1) { error("Could not find all arguments required for Jump inbuilt"); } if (holds_alternative(l.args[0])) { i = get(l.args[0]).lineNum - 2; } else { error("First argument of jump must be a line reference"); } break; /* if instruction Jumps to the line reference provided, if the boolean provided is true. */ case Instructions::If: { if (l.args.size() < 2) { error("Could not find all arguments required for If inbuilt"); } bool isTrue = false; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { if (get(get(l.args[0]).val)) isTrue = true; } else { error("First argument of if must be a bool value"); } } else { error("First argument of if must be a bool value"); } if (isTrue) { if (holds_alternative(l.args[1])) { i = get(l.args[1]).lineNum - 2; } else { error("Second argument of if must be a line reference"); } } } break; case Instructions::End: if (l.args.size() < 1) { error("Could not find all arguments required for End inbuilt"); } if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { exit(get(get(l.args[0]).val)); } else { error("First argument of end must be an int value"); } } else { error("First argument of end must be an int value"); } break; default: cout << "Still to be implemented" << endl; break; } } } /* lexer function This function takes a string (the user's program) and splits it into smaller chunks (keywords, values, words) for the parser to be able to read (see parser function below). */ vector> lexer(string in) { vector> out; vector line; string buf; bool procString = false; bool procChar = false; bool isComment = false; for (char i : in) { switch (i) { case '"': if (!isComment) { procString = !procString; buf.push_back(i); } break; case '\'': if (!isComment) { procChar = !procChar; buf.push_back(i); } break; case '\n': if (!procString && !procChar) { if (!buf.empty()) line.push_back(buf); out.push_back(line); buf.clear(); line.clear(); isComment = false; } else { if (!isComment) buf.push_back(i); } break; case '#': if (!procString && !procChar) { isComment = true; if (!buf.empty()) line.push_back(buf); out.push_back(line); buf.clear(); line.clear(); } else { buf.push_back(i); } break; case ' ': if (!procString && !procChar) { if (!buf.empty() && !isComment) line.push_back(buf); buf.clear(); } else { buf.push_back(i); } break; default: if (!isComment) buf.push_back(i); break; } } if (!buf.empty()) line.push_back(buf); out.push_back(line); return out; } /* parser function This takes multiple lines of code (in the form of vector>) and turns that human-readable code into a list of instructions. Refer to the Instruction struct above for more information. */ vector parser(vector> in) { vector out; for (vector lineTokens : in) { if (lineTokens.empty()) continue; Instruction newInst; bool firstInst = true; for (string i : lineTokens) { if (firstInst) { firstInst = false; if (i == "stdin") newInst.inst = Instructions::Stdin; else if (i == "stdout") newInst.inst = Instructions::Stdout; else if (i == "stdlnout") newInst.inst = Instructions::Stdlnout; else if (i == "jump") newInst.inst = Instructions::Jump; else if (i == "if") newInst.inst = Instructions::If; else if (i == "add") newInst.inst = Instructions::Add; else if (i == "subtract") newInst.inst = Instructions::Subtract; else if (i == "multiply") newInst.inst = Instructions::Multiply; else if (i == "divide") newInst.inst = Instructions::Divide; else if (i == "greater") newInst.inst = Instructions::Greater; else if (i == "lesser") newInst.inst = Instructions::Lesser; else if (i == "equal") newInst.inst = Instructions::Equal; else if (i == "inequal") newInst.inst = Instructions::Inequal; else if (i == "end") newInst.inst = Instructions::End; else if (i == "set") newInst.inst = Instructions::Set; else if (i == "setlist") newInst.inst = Instructions::Setlist; else if (i == "setlistat") newInst.inst = Instructions::Setlistat; else if (i == "getlistat") newInst.inst = Instructions::Getlistat; else if (i == "getlistsize") newInst.inst = Instructions::Getlistsize; else error("Unexpected token: " + i); } else { Types type = getType(i); switch (type) { case Types::Value: { ValueRef newValueRef; newValueRef.varName = i.substr(1); newInst.args.push_back(newValueRef); } break; case Types::Direct: { Direct newDirect; newDirect.varName = i.substr(1); newInst.args.push_back(newDirect); } break; case Types::Line: { Line newLine; newLine.lineNum = stoi(i.substr(1)); newInst.args.push_back(newLine); } break; case Types::ListRef: { ListRef newLR; newLR.listName = i.substr(1); newInst.args.push_back(newLR); } break; case Types::String: { Literal newLiteral; string str = i.substr(1, i.size() - 2); newLiteral.val = str; newInst.args.push_back(newLiteral); } break; case Types::Char: { Literal newLiteral; char ch = i[1]; newLiteral.val = ch; newInst.args.push_back(newLiteral); } break; case Types::Int: { Literal newLiteral; int val = stoi(i); newLiteral.val = val; newInst.args.push_back(newLiteral); } break; case Types::Double: { Literal newLiteral; double val = stod(i); newLiteral.val = val; newInst.args.push_back(newLiteral); } break; case Types::Bool: { Literal newLiteral; bool val = (i == "true"); newLiteral.val = val; newInst.args.push_back(newLiteral); } break; } } } out.push_back(newInst); } return out; } /* main function Takes input from the command line, reads the file specified by the user and starts execution of the program. See also lexer function, parser function, exec function. */ int main(int argc, char** argv) { if (argc < 2) { cout << "Usage: " << argv[0] << " (file)" << endl; exit(0); } ifstream file(argv[1]); string lns; string in; while (getline(file, lns)) { in += lns += "\n"; } exec(parser(lexer(in))); return 0; }