/*
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
#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, Gettype, Exists,
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, List, ListRef, Label, Type, Function
};
// Forward declaration of Literal for list
struct Literal;
/*
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;
};
/*
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;
};
/*
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;
};
/*
Label struct
Contains information needed to register labels
*/
struct Label {
string id;
int lineNum = -1;
};
/*
Line struct
Contains information needed to jump to lines
*/
struct Line {
int lineNum = -1;
bool isLabel = false;
string label;
};
/*
labelStack stack
Allows each function to hold it's own set of labels
*/
stack> labelStack;
/*
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;
/*
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;
};
/*
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
*/
typedef variant argument;
struct Instruction {
Instructions inst = Instructions::Empty;
vector args;
bool isLabel = false;
Label label;
};
struct FnArg {
Direct ref;
Types type;
};
/*
Function struct
Contains information needed to run a Ground function.
*/
struct Function {
Types returnType;
vector args;
vector instructions;
vector labels;
};
// 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;
// Libraries currently imported
vector libraries;
// 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" || type == "list") 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;
// Stack of strings for keeping track of which thing we're importing
stack importing;
// 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);
void preProcessLabels(vector instructions) {
map labels;
int definingFunction = 0;
for (int i = 0; i < instructions.size(); i++) {
if (instructions[i].isLabel && definingFunction == 0) {
labels[instructions[i].label.id] = i;
} else if (instructions[i].inst == Instructions::Fun) {
definingFunction++;
} else if (instructions[i].inst == Instructions::Endfun) {
definingFunction--;
}
}
labelStack.push(labels);
}
/*
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) {
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 (labelStack.top().find(ln.label) != labelStack.top().end()) {
Line newLine;
newLine.lineNum = labelStack.top()[ln.label];
l.args[j] = newLine;
} else {
if (l.inst != Instructions::Exists) 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() < 1) {
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 (argument 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;
}
}
variables[listName].val = 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 (variables.find(listref.listName) != variables.end()) {
if (holds_alternative(variables[listref.listName].val)) {
if (get(variables[listref.listName].val).val.size() > ref) {
bool existed = variables.count(var.varName) > 0;
variables[var.varName] = get(variables[listref.listName].val).val[ref];
} else {
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
}
} else {
error("Found a normal variable in place of a list");
}
} 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 (variables.find(listref.listName) != variables.end()) {
if (holds_alternative(variables[listref.listName].val)) {
if (get(variables[listref.listName].val).val.size() > ref) {
List tmpList = get(variables[listref.listName].val);
tmpList.val[ref] = value;
variables[listref.listName].val = tmpList;
} 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("First 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 (variables.find(listref.listName) != variables.end()) {
if (!holds_alternative(variables[listref.listName].val)) {
error("Variable " + listref.listName + "is not a list");
}
List tmpList = get(variables[listref.listName].val);
tmpList.val.push_back(value);
variables[listref.listName].val = tmpList;
} 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 (variables.find(ref.listName) != variables.end()) {
newLit.val = int(get(variables[ref.listName].val).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