forked from ground/ground
2385 lines
97 KiB
C++
2385 lines
97 KiB
C++
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
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 <iostream>
|
|
#include <string>
|
|
#include <variant>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <stack>
|
|
#include <fstream>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
|
|
// Headers for external libraries
|
|
#ifdef _WIN32
|
|
// Note: Windows support is experiemental. Maybe try using a superior
|
|
// operating system? (cough cough, Linux?)
|
|
#include <windows.h>
|
|
#define DLOPEN(path) LoadLibrary(path)
|
|
#define DLSYM(handle, name) GetProcAddress(handle, name)
|
|
#define DLCLOSE(handle) FreeLibrary(handle)
|
|
#define DLERROR() "Windows DLL Error"
|
|
#else
|
|
#include <dlfcn.h>
|
|
#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<Literal> 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<int, double, bool, string, char, List> 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<map<string, int>> 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<string, Literal> 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<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line> argument;
|
|
struct Instruction {
|
|
Instructions inst = Instructions::Empty;
|
|
vector<argument> 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<FnArg> args;
|
|
vector<Instruction> instructions;
|
|
vector<Label> 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<string, Function> functions;
|
|
|
|
/*
|
|
fnArgs vector
|
|
Containst the arguments to be passed to a function
|
|
*/
|
|
vector<Literal> fnArgs;
|
|
|
|
// External library functions and other things
|
|
|
|
// Handle to loaded libraries
|
|
map<string, void*> loadedLibraries;
|
|
|
|
// Map of function name to function pointer
|
|
map<string, void*> externalFunctions;
|
|
|
|
// Libraries currently imported
|
|
vector<string> libraries;
|
|
|
|
// Conversion functions
|
|
GroundValue literalToGroundValue(const Literal& lit) {
|
|
GroundValue gv;
|
|
if (holds_alternative<int>(lit.val)) {
|
|
gv.type = GROUND_INT;
|
|
gv.data.int_val = get<int>(lit.val);
|
|
} else if (holds_alternative<double>(lit.val)) {
|
|
gv.type = GROUND_DOUBLE;
|
|
gv.data.double_val = get<double>(lit.val);
|
|
} else if (holds_alternative<bool>(lit.val)) {
|
|
gv.type = GROUND_BOOL;
|
|
gv.data.bool_val = get<bool>(lit.val) ? 1 : 0;
|
|
} else if (holds_alternative<string>(lit.val)) {
|
|
gv.type = GROUND_STRING;
|
|
gv.data.string_val = const_cast<char*>(get<string>(lit.val).c_str());
|
|
} else if (holds_alternative<char>(lit.val)) {
|
|
gv.type = GROUND_CHAR;
|
|
gv.data.char_val = get<char>(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<int>(in.val)) return Types::Int;
|
|
if (holds_alternative<double>(in.val)) return Types::Double;
|
|
if (holds_alternative<bool>(in.val)) return Types::Bool;
|
|
if (holds_alternative<string>(in.val)) return Types::String;
|
|
if (holds_alternative<char>(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<string> importing;
|
|
|
|
// Forward declaration for the call instruction and use instruction
|
|
Literal exec(vector<Instruction> in);
|
|
|
|
// Forward declaration for the use instruction
|
|
vector<Instruction> parser(vector<vector<string>> in);
|
|
|
|
// Forward declaration for the use instruction
|
|
vector<vector<string>> lexer(string in);
|
|
|
|
void preProcessLabels(vector<Instruction> instructions) {
|
|
map<string, int> 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<Instruction> 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<ValueRef>(l.args[j])) {
|
|
if (variables.find(get<ValueRef>(l.args[j]).varName) != variables.end()) {
|
|
l.args[j] = variables[get<ValueRef>(l.args[j]).varName];
|
|
} else {
|
|
error("Could not find variable " + get<ValueRef>(l.args[j]).varName);
|
|
}
|
|
} else if (holds_alternative<Line>(l.args[j])) {
|
|
Line ln = get<Line>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<string>(get<Literal>(l.args[0]).val);
|
|
}
|
|
else if (holds_alternative<int>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<int>(get<Literal>(l.args[0]).val);
|
|
}
|
|
else if (holds_alternative<double>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<double>(get<Literal>(l.args[0]).val);
|
|
}
|
|
else if (holds_alternative<bool>(get<Literal>(l.args[0]).val)) {
|
|
if (get<bool>(get<Literal>(l.args[0]).val) == true) {
|
|
cout << "true";
|
|
} else {
|
|
cout << "false";
|
|
}
|
|
}
|
|
else if (holds_alternative<char>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<char>(get<Literal>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<string>(get<Literal>(l.args[0]).val) << endl;
|
|
}
|
|
else if (holds_alternative<int>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<int>(get<Literal>(l.args[0]).val) << endl;
|
|
}
|
|
else if (holds_alternative<double>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<double>(get<Literal>(l.args[0]).val) << endl;
|
|
}
|
|
else if (holds_alternative<bool>(get<Literal>(l.args[0]).val)) {
|
|
if (get<bool>(get<Literal>(l.args[0]).val) == true) {
|
|
cout << "true" << endl;
|
|
} else {
|
|
cout << "false" << endl;
|
|
}
|
|
}
|
|
else if (holds_alternative<char>(get<Literal>(l.args[0]).val)) {
|
|
cout << get<char>(get<Literal>(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<Direct>(l.args[0])) {
|
|
varRef = get<Direct>(l.args[0]);
|
|
} else {
|
|
error("First argument of set must be a direct reference");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
varContents = get<Literal>(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<ListRef>(l.args[0])) {
|
|
listName = get<ListRef>(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<Literal>(k)) {
|
|
listContents.val.push_back(get<Literal>(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<ListRef>(l.args[0])) {
|
|
listref = get<ListRef>(l.args[0]);
|
|
} else {
|
|
error("First argument of getlistat must be a list reference");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
|
|
ref = get<int>(get<Literal>(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<Direct>(l.args[2])) {
|
|
var = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of getlistat must be a direct reference");
|
|
}
|
|
|
|
if (variables.find(listref.listName) != variables.end()) {
|
|
if (holds_alternative<List>(variables[listref.listName].val)) {
|
|
if (get<List>(variables[listref.listName].val).val.size() > ref) {
|
|
bool existed = variables.count(var.varName) > 0;
|
|
variables[var.varName] = get<List>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
instr = get<string>(get<Literal>(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<Literal>(l.args[1])) {
|
|
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
|
|
ref = get<int>(get<Literal>(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<Direct>(l.args[2])) {
|
|
var = get<Direct>(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<ListRef>(l.args[0])) {
|
|
listref = get<ListRef>(l.args[0]);
|
|
} else {
|
|
error("First argument of setlistat must be a list reference");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
|
|
ref = get<int>(get<Literal>(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<Literal>(l.args[2])) {
|
|
value = get<Literal>(l.args[2]);
|
|
} else {
|
|
error("Third argument of setlistat must be a direct reference");
|
|
}
|
|
if (variables.find(listref.listName) != variables.end()) {
|
|
if (holds_alternative<List>(variables[listref.listName].val)) {
|
|
if (get<List>(variables[listref.listName].val).val.size() > ref) {
|
|
List tmpList = get<List>(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<ListRef>(l.args[0])) {
|
|
listref = get<ListRef>(l.args[0]);
|
|
} else {
|
|
error("First argument of listappend must be a list reference");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
value = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of listappend must be a direct reference");
|
|
}
|
|
|
|
if (variables.find(listref.listName) != variables.end()) {
|
|
if (!holds_alternative<List>(variables[listref.listName].val)) {
|
|
error("Variable " + listref.listName + "is not a list");
|
|
}
|
|
List tmpList = get<List>(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<ListRef>(l.args[0])) {
|
|
ref = get<ListRef>(l.args[0]);
|
|
} else {
|
|
error("First argument of getlistsize must be a list reference");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[1])) {
|
|
var = get<Direct>(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<List>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
ref = get<string>(get<Literal>(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<Direct>(l.args[1])) {
|
|
var = get<Direct>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
toConv = get<string>(get<Literal>(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<Direct>(l.args[1])) {
|
|
ref = get<Direct>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
toConv = get<string>(get<Literal>(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<Direct>(l.args[1])) {
|
|
ref = get<Direct>(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<Literal>(l.args[0])) {
|
|
toConv = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of tostring must be a literal");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[1])) {
|
|
ref = get<Direct>(l.args[1]);
|
|
} else {
|
|
error("Second argument of tostring must be a direct reference");
|
|
}
|
|
|
|
Literal newLit;
|
|
if (holds_alternative<int>(toConv.val)) {
|
|
newLit.val = to_string(get<int>(toConv.val));
|
|
} else if (holds_alternative<double>(toConv.val)) {
|
|
newLit.val = to_string(get<double>(toConv.val));
|
|
} else if (holds_alternative<string>(toConv.val)) {
|
|
newLit.val = get<string>(toConv.val);
|
|
} else if (holds_alternative<char>(toConv.val)) {
|
|
newLit.val = string().append(&get<char>(toConv.val));
|
|
} else if (holds_alternative<bool>(toConv.val)) {
|
|
if (get<bool>(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<Direct>(l.args[0])) {
|
|
Direct varRef = get<Direct>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of add must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of add must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of add must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) + get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) + get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) + get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) + double(get<int>(right.val));
|
|
} else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) {
|
|
final.val = get<string>(left.val) + get<string>(right.val);
|
|
} else if (holds_alternative<string>(left.val) && holds_alternative<char>(right.val)) {
|
|
final.val = get<string>(left.val).append(&get<char>(right.val));
|
|
} else if (holds_alternative<char>(left.val) && holds_alternative<string>(right.val)) {
|
|
final.val = string().append(&get<char>(left.val)) + get<string>(right.val);
|
|
} else if (holds_alternative<char>(left.val) && holds_alternative<char>(right.val)) {
|
|
final.val = string().append(&get<char>(left.val)).append(&get<char>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of subtract must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of subtract must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of subtract must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) - get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) - get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) - get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) - double(get<int>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of multiply must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of multiply must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of multiply must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) * get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) * get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) * get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) * double(get<int>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of divide must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of divide must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of divide must be a direct reference");
|
|
}
|
|
|
|
// Ensure we aren't dividing by zero
|
|
if (holds_alternative<int>(right.val)) {
|
|
if (get<int>(right.val) == 0) {
|
|
error("Division by zero is not allowed");
|
|
}
|
|
}
|
|
if (holds_alternative<double>(right.val)) {
|
|
if (get<double>(right.val) == 0) {
|
|
error("Division by zero is not allowed");
|
|
}
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) / get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) / get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) / get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) / double(get<int>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of equal must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of equal must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of equal must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) == get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) == get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) == get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) == double(get<int>(right.val));
|
|
} else if (holds_alternative<char>(left.val) && holds_alternative<char>(right.val)) {
|
|
final.val = get<char>(left.val) == get<char>(right.val);
|
|
} else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) {
|
|
final.val = get<string>(left.val) == get<string>(right.val);
|
|
} else if (holds_alternative<bool>(left.val) && holds_alternative<bool>(right.val)) {
|
|
final.val = get<bool>(left.val) == get<bool>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of inequal must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of inequal must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of inequal must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) != get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) != get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) != get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) != double(get<int>(right.val));
|
|
} else if (holds_alternative<char>(left.val) && holds_alternative<char>(right.val)) {
|
|
final.val = get<char>(left.val) != get<char>(right.val);
|
|
} else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) {
|
|
final.val = get<string>(left.val) != get<string>(right.val);
|
|
} else if (holds_alternative<bool>(left.val) && holds_alternative<bool>(right.val)) {
|
|
final.val = get<bool>(left.val) != get<bool>(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<Literal>(l.args[0])) {
|
|
if (holds_alternative<bool>(get<Literal>(l.args[0]).val)) {
|
|
boolean.val = !(get<bool>(get<Literal>(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<Direct>(l.args[1])) {
|
|
varRef = get<Direct>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of greater must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of greater must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of greater must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) > get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) > get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) > get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) > double(get<int>(right.val));
|
|
} else if (holds_alternative<char>(left.val) && holds_alternative<char>(right.val)) {
|
|
final.val = get<char>(left.val) > get<char>(right.val);
|
|
} else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) {
|
|
final.val = get<string>(left.val) > get<string>(right.val);
|
|
} else if (holds_alternative<bool>(left.val) && holds_alternative<bool>(right.val)) {
|
|
final.val = get<bool>(left.val) > get<bool>(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<Literal>(l.args[0])) {
|
|
left = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of lesser must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Literal>(l.args[1])) {
|
|
right = get<Literal>(l.args[1]);
|
|
} else {
|
|
error("Second argument of lesser must be a value (literal or value reference)");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[2])) {
|
|
varRef = get<Direct>(l.args[2]);
|
|
} else {
|
|
error("Third argument of lesser must be a direct reference");
|
|
}
|
|
|
|
// Figure out types and compute values
|
|
if (holds_alternative<int>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<int>(left.val) < get<int>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = get<double>(left.val) < get<double>(right.val);
|
|
} else if (holds_alternative<int>(left.val) && holds_alternative<double>(right.val)) {
|
|
final.val = double(get<int>(left.val)) < get<double>(right.val);
|
|
} else if (holds_alternative<double>(left.val) && holds_alternative<int>(right.val)) {
|
|
final.val = get<double>(left.val) < double(get<int>(right.val));
|
|
} else if (holds_alternative<char>(left.val) && holds_alternative<char>(right.val)) {
|
|
final.val = get<char>(left.val) < get<char>(right.val);
|
|
} else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) {
|
|
final.val = get<string>(left.val) < get<string>(right.val);
|
|
} else if (holds_alternative<bool>(left.val) && holds_alternative<bool>(right.val)) {
|
|
final.val = get<bool>(left.val) < get<bool>(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<Line>(l.args[0])) {
|
|
i = get<Line>(l.args[0]).lineNum - 1;
|
|
} 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<Literal>(l.args[0])) {
|
|
if (holds_alternative<bool>(get<Literal>(l.args[0]).val)) {
|
|
if (get<bool>(get<Literal>(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<Line>(l.args[1])) {
|
|
i = get<Line>(l.args[1]).lineNum - 1;
|
|
} 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<Literal>(l.args[0])) {
|
|
if (holds_alternative<int>(get<Literal>(l.args[0]).val)) {
|
|
exit(get<int>(get<Literal>(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;
|
|
case Instructions::Gettype:
|
|
if (l.args.size() < 2) {
|
|
error("Could not find all arguments required for Gettype inbuilt");
|
|
}
|
|
{
|
|
Literal val;
|
|
|
|
if (holds_alternative<Literal>(l.args[0])) {
|
|
val = get<Literal>(l.args[0]);
|
|
} else {
|
|
error("First argument of gettype must be a literal");
|
|
}
|
|
|
|
Types type = getLitType(val);
|
|
|
|
Direct ref;
|
|
|
|
if (holds_alternative<Direct>(l.args[1])) {
|
|
ref = get<Direct>(l.args[1]);
|
|
} else {
|
|
error("Second argument of gettype must be a direct reference");
|
|
}
|
|
|
|
switch (type) {
|
|
case Types::Int:
|
|
variables[ref.varName].val = "int";
|
|
break;
|
|
case Types::Double:
|
|
variables[ref.varName].val = "double";
|
|
break;
|
|
case Types::Bool:
|
|
variables[ref.varName].val = "bool";
|
|
break;
|
|
case Types::String:
|
|
variables[ref.varName].val = "string";
|
|
break;
|
|
case Types::Char:
|
|
variables[ref.varName].val = "char";
|
|
break;
|
|
default:
|
|
error("Could not get type?? This should never be reached. Please report this issue");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
case Instructions::Exists:
|
|
if (l.args.size() < 2) {
|
|
error("Could not find all arguments required for Exists inbuilt");
|
|
}
|
|
{
|
|
Direct ref2;
|
|
if (holds_alternative<Direct>(l.args[1])) {
|
|
ref2 = get<Direct>(l.args[1]);
|
|
} else {
|
|
error("Second argument of exists must be a direct reference");
|
|
}
|
|
|
|
bool exists = false;
|
|
if (holds_alternative<Direct>(l.args[0])) {
|
|
if (variables.find(get<Direct>(l.args[0]).varName) != variables.end()) {
|
|
exists = true;
|
|
}
|
|
} else if (holds_alternative<ListRef>(l.args[0])) {
|
|
if (variables.find(get<ListRef>(l.args[0]).listName) != variables.end() && holds_alternative<List>(variables[get<ListRef>(l.args[0]).listName].val)) {
|
|
exists = true;
|
|
}
|
|
} else if (holds_alternative<Line>(l.args[0])) {
|
|
Line line = get<Line>(l.args[0]);
|
|
if (line.isLabel) {
|
|
if (labelStack.top().find(line.label) != labelStack.top().end()) {
|
|
exists = true;
|
|
}
|
|
} else {
|
|
if (line.lineNum > 0 && line.lineNum <= in.size()) {
|
|
exists = true;
|
|
}
|
|
}
|
|
} else {
|
|
error("First argument of exists must be a direct, list, or line reference");
|
|
}
|
|
variables[ref2.varName].val = exists;
|
|
}
|
|
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<TypeRef>(l.args[0])) {
|
|
newFunction.returnType = get<TypeRef>(l.args[0]).type;
|
|
} else {
|
|
error("First argument of function must be a type reference");
|
|
}
|
|
|
|
string fnName;
|
|
|
|
if (holds_alternative<FunctionRef>(l.args[1])) {
|
|
fnName = get<FunctionRef>(l.args[1]).fnName;
|
|
} else {
|
|
error("Second argument of function must be a function reference");
|
|
}
|
|
|
|
if (importing.size() > 0) {
|
|
fnName = importing.top() + ":" + fnName;
|
|
}
|
|
|
|
// 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<TypeRef>(l.args[m])) {
|
|
newArg.type = get<TypeRef>(l.args[m]).type;
|
|
} else {
|
|
error("Expected type reference in function argument definition");
|
|
}
|
|
|
|
// Get direct reference
|
|
if (m + 1 < l.args.size() && holds_alternative<Direct>(l.args[m + 1])) {
|
|
newArg.ref = get<Direct>(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<Literal>(l.args[0])) {
|
|
return get<Literal>(l.args[0]);
|
|
} else if (holds_alternative<ListRef>(l.args[0])) {
|
|
return variables[get<ListRef>(l.args[0]).listName];
|
|
} 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<Literal>(l.args[0])) {
|
|
fnArgs.push_back(get<Literal>(l.args[0]));
|
|
} else if (holds_alternative<ListRef>(l.args[0])) {
|
|
fnArgs.push_back(variables[get<ListRef>(l.args[0]).listName]);
|
|
} else {
|
|
error("First argument of pusharg must be a literal or list reference");
|
|
}
|
|
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;
|
|
string returnRef;
|
|
|
|
bool expectList = true;
|
|
|
|
if (holds_alternative<FunctionRef>(l.args[0])) {
|
|
ref = get<FunctionRef>(l.args[0]);
|
|
} else {
|
|
error("First argument of call must be a function reference");
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[1])) {
|
|
returnRef = get<Direct>(l.args[1]).varName;
|
|
} else if (holds_alternative<ListRef>(l.args[1])) {
|
|
returnRef = get<ListRef>(l.args[1]).listName;
|
|
expectList = true;
|
|
} else {
|
|
error("Second argument of call must be a direct reference or list 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<GroundValue> 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] = 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<string, Literal> 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];
|
|
}
|
|
|
|
// Clear function arguments for next call
|
|
fnArgs.clear();
|
|
|
|
// Process labels
|
|
preProcessLabels(functions[ref.fnName].instructions);
|
|
|
|
// Call the function
|
|
Literal retVal = exec(functions[ref.fnName].instructions, true);
|
|
|
|
// Restore scope
|
|
variables = scopeBackup;
|
|
labelStack.pop();
|
|
|
|
// Now, assign the return value in the current scope.
|
|
if (expectList) {
|
|
variables[returnRef] = retVal;
|
|
} else {
|
|
if (holds_alternative<List>(retVal.val)) {
|
|
error("Expecting to output a normal literal to a normal literal");
|
|
} else {
|
|
variables[returnRef] = retVal;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Instructions::Use:
|
|
if (l.args.size() < 1) {
|
|
error("Could not find all arguments for Use inbuilt");
|
|
}
|
|
{
|
|
string useName;
|
|
|
|
if (holds_alternative<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
useName = get<string>(get<Literal>(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 libName = get<string>(get<Literal>(l.args[0]).val);
|
|
bool imported = false;
|
|
for (string lib : libraries) {
|
|
if (lib == libName) {
|
|
imported = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (imported) break;
|
|
|
|
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";
|
|
}
|
|
importing.push(get<string>(get<Literal>(l.args[0]).val));
|
|
vector<Instruction> parsed = parser(lexer(in));
|
|
preProcessLabels(parsed);
|
|
Literal ret = exec(parsed, false);
|
|
importing.pop();
|
|
libraries.push_back(libName);
|
|
}
|
|
break;
|
|
case Instructions::Extern:
|
|
if (l.args.size() < 1) {
|
|
error("Could not find all arguments for Extern inbuilt");
|
|
}
|
|
{
|
|
string libName;
|
|
|
|
if (holds_alternative<Literal>(l.args[0])) {
|
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
|
libName = get<string>(get<Literal>(l.args[0]).val);
|
|
} else {
|
|
error("First argument for extern requires a string literal");
|
|
}
|
|
} else {
|
|
error("First argument for extern requires a string literal");
|
|
}
|
|
|
|
bool imported = false;
|
|
for (string lib : libraries) {
|
|
if (lib == libName) {
|
|
imported = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (imported) break;
|
|
|
|
// 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[libName + ":" + 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;
|
|
}
|
|
}
|
|
if (executingFunction) error("Reached end of function and no value returned");
|
|
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<vector<string>> lexer(string in) {
|
|
vector<vector<string>> out;
|
|
vector<string> 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
|
|
and turns thructions[iat human-readable code into a list of instructions.
|
|
Refer to insructions[it.label.lineNum = i;the Instruction struct above for more information.
|
|
*/
|
|
vector<Instruction> parser(vector<vector<string>> in) {
|
|
vector<Instruction> out;
|
|
int lineNum = 0;
|
|
|
|
for (vector<string> lineTokens : in) {
|
|
lineNum ++;
|
|
Instruction newInst;
|
|
if (lineTokens.empty()) {
|
|
out.push_back(newInst);
|
|
continue;
|
|
};
|
|
bool firstInst = true;
|
|
|
|
for (string i : lineTokens) {
|
|
if (firstInst) {
|
|
firstInst = false;
|
|
if (isLabel(i)) {
|
|
newInst.isLabel = true;
|
|
newInst.label.id = i.substr(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 == "gettype") newInst.inst = Instructions::Gettype;
|
|
else if (i == "exists") newInst.inst = Instructions::Exists;
|
|
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;
|
|
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 if (type == "list") newType.type = Types::List;
|
|
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;
|
|
default:
|
|
error("This type should not be obtained in normal execution");
|
|
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);
|
|
}
|
|
|
|
// Get args
|
|
List argsList;
|
|
for (int i = 2; i < argc; i++) {
|
|
Literal lit;
|
|
lit.val = argv[i];
|
|
argsList.val.push_back(lit);
|
|
}
|
|
variables["args"].val = argsList;
|
|
|
|
ifstream file(argv[1]);
|
|
string lns;
|
|
string in;
|
|
while (getline(file, lns)) {
|
|
in += lns += "\n";
|
|
}
|
|
vector<Instruction> parsed = parser(lexer(in));
|
|
preProcessLabels(parsed);
|
|
Literal ret = exec(parsed, false);
|
|
if (holds_alternative<int>(ret.val)) {
|
|
return get<int>(ret.val);
|
|
} else {
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|