/* 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 its design, but has higher level features (more types, simpler IO, easier variables, etc) which makes 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 #include #include // Headers for external libraries #ifdef _WIN32 // Note: Windows support is experiemental. Maybe try using a superior // operating system? (cough cough, Linux?) #include #define DLOPEN(path) LoadLibrary(path) #define DLSYM(handle, name) GetProcAddress(handle, name) #define DLCLOSE(handle) FreeLibrary(handle) #define DLERROR() "Windows DLL Error" #else #include #define DLOPEN(path) dlopen(path, RTLD_LAZY) #define DLSYM(handle, name) dlsym(handle, name) #define DLCLOSE(handle) dlclose(handle) #define DLERROR() dlerror() #endif 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, Not, End, Set, Empty, Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend, Getstrcharat, Getstrsize, Stoi, Stod, Tostring, Fun, Return, Endfun, Pusharg, Call, Local, Use, Extern }; /* 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, Label, Type, Function }; /* 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; }; /* 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; }; struct TypeRef { Types type; }; struct FunctionRef { string fnName; }; /* 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; /* labels map Contains all labels made in the program, for ease of jumping around the code. */ map labels; /* 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; }; /* 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; bool isLabel = false; string label; }; /* 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; }; struct FnArg { Direct ref; Types type; }; /* Function struct Contains information needed to run a Ground function. */ struct Function { Types returnType; vector args; vector instructions; map localLabels; }; // C-compatible enum and types for developing libraries for Ground in C typedef enum { GROUND_INT, GROUND_DOUBLE, GROUND_BOOL, GROUND_STRING, GROUND_CHAR } GroundType; typedef struct { GroundType type; union { int int_val; double double_val; int bool_val; char* string_val; char char_val; } data; } GroundValue; /* functions map Contains the code of functions and types of their values */ map functions; /* fnArgs vector Containst the arguments to be passed to a function */ vector fnArgs; // External library functions and other things // Handle to loaded libraries map loadedLibraries; // Map of function name to function pointer map externalFunctions; // Conversion functions GroundValue literalToGroundValue(const Literal& lit) { GroundValue gv; if (holds_alternative(lit.val)) { gv.type = GROUND_INT; gv.data.int_val = get(lit.val); } else if (holds_alternative(lit.val)) { gv.type = GROUND_DOUBLE; gv.data.double_val = get(lit.val); } else if (holds_alternative(lit.val)) { gv.type = GROUND_BOOL; gv.data.bool_val = get(lit.val) ? 1 : 0; } else if (holds_alternative(lit.val)) { gv.type = GROUND_STRING; gv.data.string_val = const_cast(get(lit.val).c_str()); } else if (holds_alternative(lit.val)) { gv.type = GROUND_CHAR; gv.data.char_val = get(lit.val); } return gv; } Literal groundValueToLiteral(const GroundValue& gv) { Literal lit; switch (gv.type) { case GROUND_INT: lit.val = gv.data.int_val; break; case GROUND_DOUBLE: lit.val = gv.data.double_val; break; case GROUND_BOOL: lit.val = (gv.data.bool_val != 0); break; case GROUND_STRING: lit.val = string(gv.data.string_val); break; case GROUND_CHAR: lit.val = gv.data.char_val; break; } return lit; } /* 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 isLabel(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; } bool isType(string in) { if (in.size() >= 1 && in[0] == '-') { string type = in.substr(1); if (type == "string" || type == "char" || type == "bool" || type == "double" || type == "int") return true; else return false; } else return false; } bool isFunction(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 (isLabel(in)) return Types::Label; if (isListRef(in)) return Types::ListRef; if (isType(in)) return Types::Type; if (isFunction(in)) return Types::Function; error("Could not determine type of \"" + in + "\""); return Types::Int; } /* getLitType function This function determines the type of a value inside a Literal based on the holds_alternative() function. Returns a type from the Types enum class. */ Types getLitType(Literal in) { if (holds_alternative(in.val)) return Types::Int; if (holds_alternative(in.val)) return Types::Double; if (holds_alternative(in.val)) return Types::Bool; if (holds_alternative(in.val)) return Types::String; if (holds_alternative(in.val)) return Types::Char; error("Literal for some reason has a weird type. This is not an issue with your program, but an issue with the Ground interpreter."); return Types::Int; } bool processingFunction = false; string procFnName = ""; bool inFunction = false; // Forward declaration for the call instruction and use instruction Literal exec(vector in); // Forward declaration for the use instruction vector parser(vector> in); // Forward declaration for the use instruction vector> lexer(string in); /* 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. */ Literal exec(vector in, bool executingFunction) { map* currentLabels = &labels; for (auto& [fnName, fn] : functions) { if (&fn.instructions == &in) { currentLabels = &fn.localLabels; break; } } for (int i = 0; i < in.size(); i++) { Instruction l = in[i]; if (processingFunction) { if (l.inst == Instructions::Endfun) { processingFunction = false; procFnName = ""; continue; } else { functions[procFnName].instructions.push_back(l); continue; } } // Pre process value references and labels 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); } } else if (holds_alternative(l.args[j])) { Line ln = get(l.args[j]); if (ln.isLabel) { if (currentLabels->find(ln.label) != currentLabels->end()) { Line newLine; newLine.lineNum = (*currentLabels)[ln.label]; l.args[j] = newLine; } else { error("Could not find label " + ln.label); } } } } 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"); } { Direct varRef; Literal varContents; if (holds_alternative(l.args[0])) { varRef = get(l.args[0]); } 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)"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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) { bool existed = variables.count(var.varName) > 0; 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; /* getstrcharat instruction This instruction gets a character from a string at an index and saves it to a variable. */ case Instructions::Getstrcharat: if (l.args.size() < 3) { error("Could not find all arguments required for Getstrcharat inbuilt"); } { string instr; int ref; Direct var; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { instr = get(get(l.args[0]).val); } else { error("First argument of getstrcharat must be a string literal"); } } else { error("First argument of getstrcharat must be a string literal"); } 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 getstrcharat must be an integer literal"); } } else { error("Second argument of getstrcharat must be an integer literal"); } if (holds_alternative(l.args[2])) { var = get(l.args[2]); } else { error("Third argument of getstrcharat must be a direct reference"); } if (instr.size() > ref) { Literal newLit; newLit.val = instr[ref]; bool existed = variables.count(var.varName) > 0; variables[var.varName] = newLit; } else { error("Index " + to_string(ref) + " out of range of string " + instr); } } break; /* setlistat instruction This instruction sets an item in a list to be a certain value. */ case Instructions::Setlistat: if (l.args.size() < 3) { error("Could not find all arguments required for Setlistat inbuilt"); } { ListRef listref; int ref; Literal value; if (holds_alternative(l.args[0])) { listref = get(l.args[0]); } else { error("First argument of setlistat 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 setlistat must be an integer literal"); } } else { error("Second argument of setlistat must be an integer literal"); } if (holds_alternative(l.args[2])) { value = get(l.args[2]); } else { error("Third argument of setlistat must be a direct reference"); } if (lists.find(listref.listName) != lists.end()) { if (lists[listref.listName].val.size() > ref) { lists[listref.listName].val[ref] = value; } else { error("Index " + to_string(ref) + " out of range of list " + listref.listName); } } else { error("Unknown list: " + listref.listName); } } break; /* listappend instruction This instruction appends an item to a list. */ case Instructions::Listappend: if (l.args.size() < 2) { error("Could not find all arguments required for Listappend inbuilt"); } { ListRef listref; Literal value; if (holds_alternative(l.args[0])) { listref = get(l.args[0]); } else { error("Second argument of listappend must be a list reference"); } if (holds_alternative(l.args[1])) { value = get(l.args[1]); } else { error("Second argument of listappend must be a direct reference"); } if (lists.find(listref.listName) != lists.end()) { lists[listref.listName].val.push_back(value); } else { error("Unknown list: " + listref.listName); } } break; /* getlistsize instruction This instruction saves the size of a list in a variable. */ 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()); bool existed = variables.count(var.varName) > 0; variables[var.varName] = newLit; } else { error("Couldn't find the list " + ref.listName); } break; } /* getstrsize instruction This instruction saves the size of a string in a variable. */ case Instructions::Getstrsize: if (l.args.size() < 2) { error("Could not find all arguments required for Getstrsize inbuilt"); } { string ref; Direct var; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { ref = get(get(l.args[0]).val); } else { error("First argument of getlistsize must be a string value"); } } else { error("First argument of getlistsize must be a string value"); } if (holds_alternative(l.args[1])) { var = get(l.args[1]); } else { error("Second argument of getlistsize must be a direct reference"); } Literal newLit; newLit.val = int(ref.size()); bool existed = variables.count(var.varName) > 0; variables[var.varName] = newLit; break; } /* stoi instruction This function converts a string to an int, and saves it in a variable. If the string cannot be turned into an integer, there is an error. */ case Instructions::Stoi: if (l.args.size() < 2) { error("Could not find all arguments required for Stoi inbuilt"); } { string toConv; Direct ref; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { toConv = get(get(l.args[0]).val); } else { error("First argument of stoi must be a string literal"); } } else { error("First argument of stoi must be a string literal"); } if (holds_alternative(l.args[1])) { ref = get(l.args[1]); } else { error("Second argument of stoi must be a direct reference"); } if (isInt(toConv)) { Literal newLit; newLit.val = stoi(toConv); bool existed = variables.count(ref.varName) > 0; variables[ref.varName] = newLit; } else { error("Cannot convert the value " + toConv + " to an int"); } } break; /* stod instruction This function converts a string to a decimal, and saves it in a variable. If the string cannot be turned into a decimal, there is an error. */ case Instructions::Stod: if (l.args.size() < 2) { error("Could not find all arguments required for Stod inbuilt"); } { string toConv; Direct ref; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { toConv = get(get(l.args[0]).val); } else { error("First argument of stod must be a string literal"); } } else { error("First argument of stod must be a string literal"); } if (holds_alternative(l.args[1])) { ref = get(l.args[1]); } else { error("Second argument of stod must be a direct reference"); } if (isDouble(toConv) || isInt(toConv)) { Literal newLit; newLit.val = stod(toConv); bool existed = variables.count(ref.varName) > 0; variables[ref.varName] = newLit; } else { error("Cannot convert the value " + toConv + " to a decimal"); } } break; /* tostring instruction This function converts any type to a string, and saves it in a variable. */ case Instructions::Tostring: if (l.args.size() < 2) { error("Could not find all arguments required for Tostring inbuilt"); } { Literal toConv; Direct ref; if (holds_alternative(l.args[0])) { toConv = get(l.args[0]); } else { error("First argument of tostring must be a literal"); } if (holds_alternative(l.args[1])) { ref = get(l.args[1]); } else { error("Second argument of tostring must be a direct reference"); } Literal newLit; if (holds_alternative(toConv.val)) { newLit.val = to_string(get(toConv.val)); } else if (holds_alternative(toConv.val)) { newLit.val = to_string(get(toConv.val)); } else if (holds_alternative(toConv.val)) { newLit.val = get(toConv.val); } else if (holds_alternative(toConv.val)) { newLit.val = string().append(&get(toConv.val)); } else if (holds_alternative(toConv.val)) { if (get(toConv.val)) { newLit.val = "true"; } else { newLit.val = "false"; } } bool existed = variables.count(ref.varName) > 0; variables[ref.varName] = newLit; } 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])) { Direct varRef = get(l.args[0]); string userIn; getline(cin, userIn); Literal userLit; userLit.val = userIn; bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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 if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = get(left.val).append(&get(right.val)); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = string().append(&get(left.val)) + get(right.val); } else if (holds_alternative(left.val) && holds_alternative(right.val)) { final.val = string().append(&get(left.val)).append(&get(right.val)); } else { error("Cannot add those two values"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.varName] = final; } break; /* not instruction Negates a boolean. */ case Instructions::Not: if (l.args.size() < 2) { error("Could not find all arguments required for Not inbuilt"); } { Literal boolean; Direct varRef; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { boolean.val = !(get(get(l.args[0]).val)); } else { error("First argument of not must be a boolean literal"); } } else { error("First argument of not must be a boolean literal"); } if (holds_alternative(l.args[1])) { varRef = get(l.args[1]); } else { error("Second argument of not must be a direct reference"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.varName] = boolean; } 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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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"); } { Direct varRef; 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])) { varRef = get(l.args[2]); } 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"); } bool existed = variables.count(varRef.varName) > 0; variables[varRef.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; /* end instruction Ends execution of the code, eith the status code provided. */ 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; /* fun instruction Allows functions to be defined. */ case Instructions::Fun: if (l.args.size() < 2) { error("Could not find all arguments required for Fun inbuilt"); } { Function newFunction; if (holds_alternative(l.args[0])) { newFunction.returnType = get(l.args[0]).type; } else { error("First argument of function must be a type reference"); } string fnName; if (holds_alternative(l.args[1])) { fnName = get(l.args[1]).fnName; } else { error("Second argument of function must be a function reference"); } // Parse function arguments (type-direct pairs) if ((l.args.size() - 2) % 2 != 0) { error("Function arguments must be in type-direct pairs"); } for (int m = 2; m < l.args.size(); m += 2) { FnArg newArg; // Get type if (holds_alternative(l.args[m])) { newArg.type = get(l.args[m]).type; } else { error("Expected type reference in function argument definition"); } // Get direct reference if (m + 1 < l.args.size() && holds_alternative(l.args[m + 1])) { newArg.ref = get(l.args[m + 1]); } else { error("Expected direct reference after type reference in function argument definition"); } newFunction.args.push_back(newArg); } functions[fnName] = newFunction; processingFunction = true; procFnName = fnName; } break; /* return instruction Exits a function. */ case Instructions::Return: if (l.args.size() < 1) { error("Could not find all arguments required for Return inbuilt"); } if (holds_alternative(l.args[0])) { return get(l.args[0]); } else { error("First argument of return must be a literal value/value reference"); } break; /* endfun instruction This should not be reached during normal execution. */ case Instructions::Endfun: error("No function is being defined. Cannot end function declaration here"); break; /* pusharg instruction This instruction makes new arguments avaliable for functions. */ case Instructions::Pusharg: if (l.args.size() < 1) { error("Could not find all arguments required for Endfun inbuilt"); } if (holds_alternative(l.args[0])) { fnArgs.push_back(get(l.args[0])); } else { error("First argument of pusharg must be a literal"); } break; /* call instruction This instruction calls a function, and makes the arguments avaliable for it. */ case Instructions::Call: { if (l.args.size() < 2) { error("Could not find all arguments required for Call inbuilt"); } FunctionRef ref; Direct returnRef; if (holds_alternative(l.args[0])) { ref = get(l.args[0]); } else { error("First argument of call must be a function reference"); } if (holds_alternative(l.args[1])) { returnRef = get(l.args[1]); } else { error("Second argument of call must be a direct reference"); } // Check for external function if (externalFunctions.find(ref.fnName) != externalFunctions.end()) { // Call external function typedef GroundValue (*ExtFunc)(GroundValue*, int); ExtFunc extFunc = (ExtFunc)externalFunctions[ref.fnName]; // Convert arguments vector gvArgs; for (const Literal& arg : fnArgs) { gvArgs.push_back(literalToGroundValue(arg)); } // Call function GroundValue result = extFunc(gvArgs.data(), gvArgs.size()); // Convert result back Literal resultLit = groundValueToLiteral(result); // Clear arguments and store result fnArgs.clear(); variables[returnRef.varName] = resultLit; break; } // Check if function exists if (functions.find(ref.fnName) == functions.end()) { error("Function " + ref.fnName + " not found"); } // Check argument count if (fnArgs.size() != functions[ref.fnName].args.size()) { error("Function " + ref.fnName + " expects " + to_string(functions[ref.fnName].args.size()) + " arguments, got " + to_string(fnArgs.size())); } // Create backup of variables to be restored map scopeBackup = variables; // Create function arguments with type checking for (int m = 0; m < functions[ref.fnName].args.size(); m++) { FnArg arg = functions[ref.fnName].args[m]; // Type checking - now with error reporting if (arg.type != getLitType(fnArgs[m])) { error("Function " + ref.fnName + " argument " + to_string(m + 1) + " type mismatch. Expected type does not match provided argument type."); } // Create the variable variables[arg.ref.varName] = fnArgs[m]; } // Call the function Literal retVal = exec(functions[ref.fnName].instructions, true); // Restore scope variables = scopeBackup; // Clear function arguments for next call fnArgs.clear(); // Now, assign the return value in the current scope. bool existed = variables.count(returnRef.varName) > 0; variables[returnRef.varName] = retVal; } break; case Instructions::Use: if (l.args.size() < 1) { error("Could not find all arguments for Use inbuilt"); } { string useName; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { useName = get(get(l.args[0]).val) + ".grnd"; } else { error("First argument for use requires a string literal"); } } else { error("First argument for use requires a string literal"); } string groundLibsDir = getenv("GROUND_LIBS"); if (filesystem::exists(useName)) { } else if (groundLibsDir != "" && filesystem::exists(groundLibsDir + useName)) { useName = groundLibsDir + useName; } else { error("Could not find external Ground library in $GROUND_LIBS or current directory."); } ifstream file(useName); string lns; string in; while (getline(file, lns)) { in += lns += "\n"; } Literal ret = exec(parser(lexer(in)), false); } break; case Instructions::Extern: if (l.args.size() < 1) { error("Could not find all arguments for Extern inbuilt"); } { string libName; if (holds_alternative(l.args[0])) { if (holds_alternative(get(l.args[0]).val)) { libName = get(get(l.args[0]).val); } else { error("First argument for extern requires a string literal"); } } else { error("First argument for extern requires a string literal"); } // Add appropriate extension string fullLibName = libName; #ifdef _WIN32 fullLibName += ".dll"; #elif __APPLE__ fullLibName += ".dylib"; #else fullLibName += ".so"; #endif // Check multiple locations for the library string libPath; bool found = false; // Check GROUND_LIBS directory const char* groundLibsDir = getenv("GROUND_LIBS"); if (groundLibsDir) { string envPath = string(groundLibsDir); // Add trailing slash if not present if (!envPath.empty() && envPath.back() != '/' && envPath.back() != '\\') { envPath += "/"; } string fullEnvPath = envPath + fullLibName; if (filesystem::exists(fullEnvPath)) { libPath = fullEnvPath; found = true; } } if (!found) { error("Could not find external library: " + fullLibName + " (searched current directory and GROUND_LIBS)"); } // Try to open the library void* handle = DLOPEN(libPath.c_str()); if (!handle) { error("Failed to load library " + libPath + ": " + string(DLERROR())); } // Store handle for cleanup later loadedLibraries[libName] = handle; // Get required functions typedef const char** (*GetFunctionsFunc)(); typedef void* (*GetFunctionFunc)(const char*); GetFunctionsFunc getFunctions = (GetFunctionsFunc)DLSYM(handle, "ground_get_functions"); GetFunctionFunc getFunction = (GetFunctionFunc)DLSYM(handle, "ground_get_function"); if (!getFunctions || !getFunction) { error("Library " + libName + " is not Ground-compatible (missing required functions: ground_get_functions or ground_get_function)"); } // Optional initialization typedef void (*InitFunc)(); InitFunc initFunc = (InitFunc)DLSYM(handle, "ground_lib_init"); if (initFunc) { initFunc(); } // Register all functions const char** functionNames = getFunctions(); if (!functionNames) { error("Library " + libName + " returned null function list"); } int functionCount = 0; for (int i = 0; functionNames[i] != nullptr; i++) { void* funcPtr = getFunction(functionNames[i]); if (funcPtr) { externalFunctions[string(functionNames[i])] = funcPtr; functionCount++; } else { error("Failed to get function pointer for: " + string(functionNames[i])); } } if (functionCount == 0) { error("No functions were loaded from library: " + libName); } } break; default: cout << "Still to be implemented" << endl; break; } } Literal retLiteral; return retLiteral; } /* 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) { if (procChar) { buf.push_back(i); } else { procString = !procString; buf.push_back(i); } } break; case '\'': if (!isComment) { if (procString) { buf.push_back(i); } else { 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; int lineNum = 1; int functionInstructionIndex = 0; for (vector lineTokens : in) { lineNum ++; Instruction newInst; if (lineTokens.empty()) { if (processingFunction) functionInstructionIndex ++; out.push_back(newInst); continue; }; bool firstInst = true; for (string i : lineTokens) { if (firstInst) { firstInst = false; if (isLabel(i)) { if (processingFunction) { functions[procFnName].localLabels[i.substr(1)] = functionInstructionIndex; } else { labels[i.substr(1)] = lineNum - 1; } } else 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 == "not") newInst.inst = Instructions::Not; 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 if (i == "listappend") newInst.inst = Instructions::Listappend; else if (i == "getstrsize") newInst.inst = Instructions::Getstrsize; else if (i == "getstrcharat") newInst.inst = Instructions::Getstrcharat; else if (i == "stoi") newInst.inst = Instructions::Stoi; else if (i == "stod") newInst.inst = Instructions::Stod; else if (i == "tostring") newInst.inst = Instructions::Tostring; else if (i == "fun") { newInst.inst = Instructions::Fun; functionInstructionIndex = 0; } else if (i == "return") newInst.inst = Instructions::Return; else if (i == "endfun") newInst.inst = Instructions::Endfun; else if (i == "pusharg") newInst.inst = Instructions::Pusharg; else if (i == "call") newInst.inst = Instructions::Call; else if (i == "use") newInst.inst = Instructions::Use; else if (i == "extern") newInst.inst = Instructions::Extern; 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::Label: error("Label should be defined as first token"); break; case Types::Type: { TypeRef newType; string type = i.substr(1); if (type == "string") newType.type = Types::String; else if (type == "char") newType.type = Types::Char; else if (type == "double") newType.type = Types::Double; else if (type == "int") newType.type = Types::Int; else if (type == "bool") newType.type = Types::Bool; else error("Ground could not find type. This is an error with the interpreter, not your code. This line of code should never be reached."); newInst.args.push_back(newType); } break; case Types::Function: { FunctionRef newFunction; newFunction.fnName = i.substr(1); newInst.args.push_back(newFunction); } break; case Types::Line: { Line newLine; if (isInt(i.substr(1))) newLine.lineNum = stoi(i.substr(1)); else { newLine.isLabel = true; newLine.label = i.substr(1); newLine.lineNum = 0; } 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; } } } if (processingFunction) functionInstructionIndex++; 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); } // Get args List argsList; for (int i = 2; i < argc; i++) { Literal lit; lit.val = argv[i]; argsList.val.push_back(lit); } lists["args"] = argsList; ifstream file(argv[1]); string lns; string in; while (getline(file, lns)) { in += lns += "\n"; } Literal ret = exec(parser(lexer(in)), false); if (holds_alternative(ret.val)) { return get(ret.val); } else { return 0; } return 0; }