2025-08-09 20:33:08 +10:00
|
|
|
/*
|
|
|
|
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
|
2025-08-11 08:57:45 +10:00
|
|
|
Assembly in its design, but has higher level features (more types,
|
|
|
|
simpler IO, easier variables, etc) which makes it easy to use.
|
2025-08-09 20:33:08 +10:00
|
|
|
|
|
|
|
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 <fstream>
|
|
|
|
|
|
|
|
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 {
|
2025-08-10 13:31:28 +10:00
|
|
|
Jump, If,
|
|
|
|
Stdout, Stdin, Stdlnout,
|
|
|
|
Add, Subtract, Multiply, Divide,
|
|
|
|
Equal, Inequal, Greater, Lesser,
|
|
|
|
End, Set, Empty,
|
2025-08-11 10:07:05 +10:00
|
|
|
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
|
|
|
|
Getstrcharat, Getstrsize,
|
2025-08-11 14:12:25 +10:00
|
|
|
Stoi, Stod, Tostring,
|
2025-08-11 10:07:05 +10:00
|
|
|
Fun, Endfun, Pusharg, Call
|
2025-08-09 20:33:08 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
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 {
|
2025-08-10 15:42:52 +10:00
|
|
|
Int, Double, String, Char, Bool, Value, Direct, Line, ListRef, Label
|
2025-08-09 20:33:08 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
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> val;
|
|
|
|
};
|
|
|
|
|
2025-08-10 13:31:28 +10:00
|
|
|
/*
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2025-08-09 20:33:08 +10:00
|
|
|
/*
|
|
|
|
variables map
|
|
|
|
Contains all variables made while running the program. See also Literal struct.
|
|
|
|
*/
|
|
|
|
map<string, Literal> variables;
|
|
|
|
|
2025-08-10 13:31:28 +10:00
|
|
|
|
|
|
|
/*
|
|
|
|
lists map
|
|
|
|
Contains all lists made while running the program. See also List struct.
|
|
|
|
*/
|
|
|
|
map<string, List> lists;
|
|
|
|
|
2025-08-10 15:42:52 +10:00
|
|
|
/*
|
|
|
|
labels map
|
|
|
|
Contains all labels made in the program, for ease of jumping around the code.
|
|
|
|
*/
|
|
|
|
map<string, int> labels;
|
|
|
|
|
2025-08-09 20:33:08 +10:00
|
|
|
/*
|
|
|
|
ValueRef struct
|
|
|
|
If the program being executed makes a value reference, it is stored in a ValueRef
|
|
|
|
struct. For example, if the following line was written:
|
|
|
|
stdin &myVar
|
|
|
|
The ValueRef struct in the instruction should look like this:
|
|
|
|
{
|
|
|
|
varName = "myVar";
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
struct ValueRef {
|
|
|
|
string varName;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Direct struct
|
|
|
|
If the program being executed makes a direct reference, it is stored in a Direct
|
|
|
|
struct. For example, if the following line was written:
|
|
|
|
stdin &myVar
|
|
|
|
The Direct struct in the instruction should look like this:
|
|
|
|
{
|
|
|
|
varName = "myVar";
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
struct Direct {
|
|
|
|
string varName;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Line struct
|
|
|
|
If the program being executed makes a line reference, it is stored in a Line
|
|
|
|
struct. For example, if the following line was written:
|
|
|
|
jump %10
|
|
|
|
The Line struct in the instruction should look like this:
|
|
|
|
{
|
|
|
|
lineNum = 10;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
struct Line {
|
|
|
|
int lineNum;
|
2025-08-10 15:42:52 +10:00
|
|
|
bool isLabel = false;
|
|
|
|
string label;
|
2025-08-09 20:33:08 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Instruction struct
|
|
|
|
An instruction usually corresponds to a line in the program being interpreted.
|
|
|
|
For example, if the following line was written:
|
|
|
|
add 5 $myVar &outVar
|
|
|
|
The instruction should look like this:
|
|
|
|
{
|
|
|
|
inst = Instructions::Add;
|
|
|
|
args = {
|
|
|
|
Literal {
|
|
|
|
val = 5
|
|
|
|
},
|
|
|
|
ValueRef {
|
|
|
|
varName = "myVar"
|
|
|
|
},
|
|
|
|
Direct {
|
|
|
|
varName = "outVar"
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
inst starts as empty, so empty lines and commented out lines do not get in the
|
|
|
|
way of jump and if.
|
|
|
|
See also: Instructions enum class, Literal struct, ValueRef struct, Direct struct,
|
|
|
|
Line struct, exec function, parser function
|
|
|
|
*/
|
|
|
|
struct Instruction {
|
|
|
|
Instructions inst = Instructions::Empty;
|
2025-08-10 13:31:28 +10:00
|
|
|
vector<variant<Literal, ValueRef, ListRef, Direct, Line>> args;
|
2025-08-09 20:33:08 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
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);
|
2025-08-09 19:52:49 +10:00
|
|
|
if (stod(in) != stoi(in)) return false;
|
2025-08-09 20:33:08 +10:00
|
|
|
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) {
|
2025-08-10 13:31:28 +10:00
|
|
|
if (in.size() >= 1 && in[0] == '%') return true;
|
|
|
|
else return false;
|
|
|
|
}
|
|
|
|
|
2025-08-10 15:42:52 +10:00
|
|
|
bool isLabel(string in) {
|
|
|
|
if (in.size() >= 1 && in[0] == '@') return true;
|
|
|
|
else return false;
|
|
|
|
}
|
|
|
|
|
2025-08-10 13:31:28 +10:00
|
|
|
bool isListRef(string in) {
|
|
|
|
if (in.size() >= 1 && in[0] == '*') return true;
|
2025-08-09 20:33:08 +10:00
|
|
|
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;
|
2025-08-10 15:42:52 +10:00
|
|
|
if (isLabel(in)) return Types::Label;
|
2025-08-10 13:31:28 +10:00
|
|
|
if (isListRef(in)) return Types::ListRef;
|
2025-08-09 20:33:08 +10:00
|
|
|
error("Could not determine type of \"" + in + "\"");
|
|
|
|
return Types::Int;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
exec function
|
|
|
|
This function takes a list of instructions (see Instruction struct above and parser
|
|
|
|
function below) and acts upon their properties. This is the main interpreter
|
|
|
|
function for the program.
|
|
|
|
*/
|
|
|
|
void exec(vector<Instruction> in) {
|
|
|
|
for (int i = 0; i < in.size(); i++) {
|
|
|
|
Instruction l = in[i];
|
2025-08-10 15:42:52 +10:00
|
|
|
// Pre process value references and labels
|
2025-08-09 20:33:08 +10:00
|
|
|
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);
|
|
|
|
}
|
2025-08-10 15:42:52 +10:00
|
|
|
} else if (holds_alternative<Line>(l.args[j])) {
|
|
|
|
Line ln = get<Line>(l.args[j]);
|
|
|
|
if (ln.isLabel) {
|
|
|
|
if (labels.find(ln.label) != labels.end()) {
|
|
|
|
Line newLine;
|
|
|
|
newLine.lineNum = labels[ln.label];
|
|
|
|
l.args[j] = newLine;
|
|
|
|
} else {
|
|
|
|
error("Could not find label " + ln.label);
|
|
|
|
}
|
|
|
|
}
|
2025-08-09 20:33:08 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal varContents;
|
|
|
|
|
|
|
|
if (holds_alternative<Direct>(l.args[0])) {
|
|
|
|
varName = get<Direct>(l.args[0]).varName;
|
|
|
|
} 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)");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = varContents;
|
|
|
|
}
|
|
|
|
break;
|
2025-08-10 13:31:28 +10:00
|
|
|
/*
|
|
|
|
setlist instruction
|
|
|
|
This instruction takes a potentially infinite amount of arguments and
|
|
|
|
saves them to a list (vector) with name listName.
|
|
|
|
*/
|
|
|
|
case Instructions::Setlist:
|
|
|
|
if (l.args.size() < 2) {
|
|
|
|
error("Could not find all arguments required for Setlist inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string listName;
|
|
|
|
List listContents;
|
|
|
|
|
|
|
|
if (holds_alternative<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 (variant<Literal, ValueRef, ListRef, Direct, Line> 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lists[listName] = listContents;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
getlistat instruction
|
|
|
|
This instruction gets a list item from a list and an index and saves the
|
|
|
|
value to a variable.
|
|
|
|
*/
|
|
|
|
case Instructions::Getlistat:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Getlistat inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ListRef listref;
|
|
|
|
int ref;
|
|
|
|
Direct var;
|
|
|
|
|
|
|
|
if (holds_alternative<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 (lists.find(listref.listName) != lists.end()) {
|
|
|
|
if (lists[listref.listName].val.size() > ref) {
|
|
|
|
variables[var.varName] = lists[listref.listName].val[ref];
|
|
|
|
} else {
|
|
|
|
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error("Unknown list: " + listref.listName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2025-08-11 10:07:05 +10:00
|
|
|
/*
|
|
|
|
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 getlistat must be a string literal");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error("First argument of getlistat 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 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 (instr.size() > ref) {
|
|
|
|
Literal newLit;
|
|
|
|
newLit.val = instr[ref];
|
|
|
|
variables[var.varName] = newLit;
|
|
|
|
} else {
|
|
|
|
error("Index " + to_string(ref) + " out of range of string " + instr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2025-08-11 08:57:45 +10:00
|
|
|
/*
|
|
|
|
setlistat instruction
|
|
|
|
This instruction sets an item in a list to be a certain value.
|
|
|
|
*/
|
2025-08-10 16:08:56 +10:00
|
|
|
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 (lists.find(listref.listName) != lists.end()) {
|
|
|
|
if (lists[listref.listName].val.size() > ref) {
|
|
|
|
lists[listref.listName].val[ref] = value;
|
|
|
|
} else {
|
|
|
|
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error("Unknown list: " + listref.listName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2025-08-11 08:57:45 +10:00
|
|
|
/*
|
|
|
|
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("Second 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 (lists.find(listref.listName) != lists.end()) {
|
|
|
|
lists[listref.listName].val.push_back(value);
|
|
|
|
} else {
|
|
|
|
error("Unknown list: " + listref.listName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2025-08-10 16:08:56 +10:00
|
|
|
/*
|
|
|
|
getlistsize instruction
|
|
|
|
This instruction saves the size of a list in a variable.
|
|
|
|
*/
|
2025-08-10 13:31:28 +10:00
|
|
|
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 (lists.find(ref.listName) != lists.end()) {
|
|
|
|
newLit.val = int(lists[ref.listName].val.size());
|
|
|
|
variables[var.varName] = newLit;
|
|
|
|
} else {
|
|
|
|
error("Couldn't find the list " + ref.listName);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2025-08-09 20:33:08 +10:00
|
|
|
/*
|
2025-08-11 10:07:05 +10:00
|
|
|
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());
|
|
|
|
variables[var.varName] = newLit;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2025-08-11 14:12:25 +10:00
|
|
|
/*
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[ref.varName] = newLit;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
2025-08-11 10:07:05 +10:00
|
|
|
/*
|
2025-08-09 20:33:08 +10:00
|
|
|
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) {
|
2025-08-10 13:31:28 +10:00
|
|
|
error("Could not find all arguments required for Stdin inbuilt");
|
2025-08-09 20:33:08 +10:00
|
|
|
}
|
|
|
|
if (holds_alternative<Direct>(l.args[0])) {
|
|
|
|
string userIn;
|
|
|
|
getline(cin, userIn);
|
|
|
|
Literal userLit;
|
|
|
|
userLit.val = userIn;
|
|
|
|
variables[get<Direct>(l.args[0]).varName] = userLit;
|
|
|
|
} else {
|
|
|
|
error("First argument of stdin must be a direct reference");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
add instruction
|
|
|
|
This instruction adds two values (can be numbers or strings) and outputs
|
|
|
|
it to the direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Add:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Add inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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);
|
2025-08-11 10:07:05 +10:00
|
|
|
} 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));
|
2025-08-09 20:33:08 +10:00
|
|
|
} else {
|
|
|
|
error("Cannot add those two values");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
subtract instruction
|
|
|
|
This instruction subtracts two values and outputs it to the
|
|
|
|
direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Subtract:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Subtract inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
multiply instruction
|
|
|
|
This instruction multiplies two numbers and outputs it to the
|
|
|
|
direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Multiply:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Multiply inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
divide instruction
|
|
|
|
This instruction divides two numbers and outputs it to the direct
|
|
|
|
reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Divide:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Divide inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} else {
|
|
|
|
error("Third argument of divide must be a direct reference");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure we aren't dividing by zero
|
|
|
|
if (holds_alternative<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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
equal instruction
|
|
|
|
This instruction checks if two values are equal and outputs a boolean
|
|
|
|
to the direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Equal:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Equal inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
inequal instruction
|
|
|
|
This instruction checks if two values are not equal and outputs a boolean
|
|
|
|
to the direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Inequal:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Inequal inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
greater instruction
|
|
|
|
This instruction checks if the left value is greater than the right value
|
|
|
|
and outputs a boolean to the direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Greater:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Greater inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
lesser instruction
|
|
|
|
This instruction checks if the left value is lesser than the right value
|
|
|
|
and outputs a boolean to the direct reference given.
|
|
|
|
*/
|
|
|
|
case Instructions::Lesser:
|
|
|
|
if (l.args.size() < 3) {
|
|
|
|
error("Could not find all arguments required for Lesser inbuilt");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
string varName;
|
|
|
|
Literal left;
|
|
|
|
Literal right;
|
|
|
|
Literal final;
|
|
|
|
|
|
|
|
if (holds_alternative<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])) {
|
|
|
|
varName = get<Direct>(l.args[2]).varName;
|
|
|
|
} 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
variables[varName] = final;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
jump instruction
|
|
|
|
Jumps to the line reference provided.
|
|
|
|
*/
|
|
|
|
case Instructions::Jump:
|
|
|
|
if (l.args.size() < 1) {
|
|
|
|
error("Could not find all arguments required for Jump inbuilt");
|
|
|
|
}
|
|
|
|
if (holds_alternative<Line>(l.args[0])) {
|
|
|
|
i = get<Line>(l.args[0]).lineNum - 2;
|
|
|
|
} else {
|
|
|
|
error("First argument of jump must be a line reference");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
if instruction
|
|
|
|
Jumps to the line reference provided, if the boolean provided is true.
|
|
|
|
*/
|
|
|
|
case Instructions::If:
|
|
|
|
{
|
|
|
|
if (l.args.size() < 2) {
|
|
|
|
error("Could not find all arguments required for If inbuilt");
|
|
|
|
}
|
|
|
|
bool isTrue = false;
|
|
|
|
if (holds_alternative<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 - 2;
|
|
|
|
} else {
|
|
|
|
error("Second argument of if must be a line reference");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Instructions::End:
|
|
|
|
if (l.args.size() < 1) {
|
|
|
|
error("Could not find all arguments required for End inbuilt");
|
|
|
|
}
|
|
|
|
if (holds_alternative<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;
|
|
|
|
default:
|
|
|
|
cout << "Still to be implemented" << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
lexer function
|
|
|
|
This function takes a string (the user's program) and splits it into smaller chunks
|
|
|
|
(keywords, values, words) for the parser to be able to read (see parser function
|
|
|
|
below).
|
|
|
|
*/
|
|
|
|
vector<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) {
|
|
|
|
procString = !procString;
|
|
|
|
buf.push_back(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
if (!isComment) {
|
|
|
|
procChar = !procChar;
|
|
|
|
buf.push_back(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
if (!procString && !procChar) {
|
|
|
|
if (!buf.empty()) line.push_back(buf);
|
|
|
|
out.push_back(line);
|
|
|
|
buf.clear();
|
|
|
|
line.clear();
|
|
|
|
isComment = false;
|
|
|
|
} else {
|
|
|
|
if (!isComment) buf.push_back(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '#':
|
|
|
|
if (!procString && !procChar) {
|
|
|
|
isComment = true;
|
|
|
|
if (!buf.empty()) line.push_back(buf);
|
|
|
|
out.push_back(line);
|
|
|
|
buf.clear();
|
|
|
|
line.clear();
|
|
|
|
} else {
|
|
|
|
buf.push_back(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
if (!procString && !procChar) {
|
|
|
|
if (!buf.empty() && !isComment) line.push_back(buf);
|
|
|
|
buf.clear();
|
|
|
|
} else {
|
|
|
|
buf.push_back(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!isComment) buf.push_back(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!buf.empty()) line.push_back(buf);
|
|
|
|
out.push_back(line);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
parser function
|
|
|
|
This takes multiple lines of code (in the form of vector<vector<string>>)
|
|
|
|
and turns that human-readable code into a list of instructions.
|
|
|
|
Refer to the Instruction struct above for more information.
|
|
|
|
*/
|
|
|
|
vector<Instruction> parser(vector<vector<string>> in) {
|
|
|
|
vector<Instruction> out;
|
2025-08-10 15:42:52 +10:00
|
|
|
|
|
|
|
int lineNum = 1;
|
2025-08-09 20:33:08 +10:00
|
|
|
for (vector<string> lineTokens : in) {
|
2025-08-10 15:42:52 +10:00
|
|
|
lineNum ++;
|
2025-08-09 20:33:08 +10:00
|
|
|
Instruction newInst;
|
2025-08-10 15:42:52 +10:00
|
|
|
if (lineTokens.empty()) {
|
|
|
|
out.push_back(newInst);
|
|
|
|
continue;
|
|
|
|
};
|
2025-08-09 20:33:08 +10:00
|
|
|
bool firstInst = true;
|
|
|
|
|
|
|
|
for (string i : lineTokens) {
|
|
|
|
if (firstInst) {
|
|
|
|
firstInst = false;
|
2025-08-10 15:42:52 +10:00
|
|
|
if (isLabel(i)) {
|
|
|
|
labels[i.substr(1)] = lineNum;
|
|
|
|
out.push_back(newInst);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (i == "stdin") newInst.inst = Instructions::Stdin;
|
2025-08-09 20:33:08 +10:00
|
|
|
else if (i == "stdout") newInst.inst = Instructions::Stdout;
|
|
|
|
else if (i == "stdlnout") newInst.inst = Instructions::Stdlnout;
|
|
|
|
else if (i == "jump") newInst.inst = Instructions::Jump;
|
|
|
|
else if (i == "if") newInst.inst = Instructions::If;
|
|
|
|
else if (i == "add") newInst.inst = Instructions::Add;
|
|
|
|
else if (i == "subtract") newInst.inst = Instructions::Subtract;
|
|
|
|
else if (i == "multiply") newInst.inst = Instructions::Multiply;
|
|
|
|
else if (i == "divide") newInst.inst = Instructions::Divide;
|
|
|
|
else if (i == "greater") newInst.inst = Instructions::Greater;
|
|
|
|
else if (i == "lesser") newInst.inst = Instructions::Lesser;
|
|
|
|
else if (i == "equal") newInst.inst = Instructions::Equal;
|
|
|
|
else if (i == "inequal") newInst.inst = Instructions::Inequal;
|
|
|
|
else if (i == "end") newInst.inst = Instructions::End;
|
|
|
|
else if (i == "set") newInst.inst = Instructions::Set;
|
2025-08-10 13:31:28 +10:00
|
|
|
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;
|
2025-08-11 10:07:05 +10:00
|
|
|
else if (i == "listappend") newInst.inst = Instructions::Listappend;
|
|
|
|
else if (i == "getstrsize") newInst.inst = Instructions::Getstrsize;
|
|
|
|
else if (i == "getstrcharat") newInst.inst = Instructions::Getstrcharat;
|
2025-08-11 14:12:25 +10:00
|
|
|
else if (i == "stoi") newInst.inst = Instructions::Stoi;
|
|
|
|
else if (i == "stod") newInst.inst = Instructions::Stod;
|
|
|
|
else if (i == "tostring") newInst.inst = Instructions::Tostring;
|
2025-08-09 20:33:08 +10:00
|
|
|
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;
|
2025-08-10 15:42:52 +10:00
|
|
|
case Types::Label:
|
|
|
|
error("Label should be defined as first token");
|
|
|
|
break;
|
2025-08-09 20:33:08 +10:00
|
|
|
case Types::Line:
|
|
|
|
{
|
|
|
|
Line newLine;
|
2025-08-10 15:42:52 +10:00
|
|
|
if (isInt(i.substr(1))) newLine.lineNum = stoi(i.substr(1));
|
|
|
|
else {
|
|
|
|
newLine.isLabel = true;
|
|
|
|
newLine.label = i.substr(1);
|
|
|
|
newLine.lineNum = 0;
|
|
|
|
}
|
2025-08-09 20:33:08 +10:00
|
|
|
newInst.args.push_back(newLine);
|
|
|
|
}
|
|
|
|
break;
|
2025-08-10 13:31:28 +10:00
|
|
|
case Types::ListRef:
|
|
|
|
{
|
|
|
|
ListRef newLR;
|
|
|
|
newLR.listName = i.substr(1);
|
|
|
|
newInst.args.push_back(newLR);
|
|
|
|
}
|
|
|
|
break;
|
2025-08-09 20:33:08 +10:00
|
|
|
case Types::String:
|
|
|
|
{
|
|
|
|
Literal newLiteral;
|
|
|
|
string str = i.substr(1, i.size() - 2);
|
|
|
|
newLiteral.val = str;
|
|
|
|
newInst.args.push_back(newLiteral);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Types::Char:
|
|
|
|
{
|
|
|
|
Literal newLiteral;
|
|
|
|
char ch = i[1];
|
|
|
|
newLiteral.val = ch;
|
|
|
|
newInst.args.push_back(newLiteral);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Types::Int:
|
|
|
|
{
|
|
|
|
Literal newLiteral;
|
|
|
|
int val = stoi(i);
|
|
|
|
newLiteral.val = val;
|
|
|
|
newInst.args.push_back(newLiteral);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Types::Double:
|
|
|
|
{
|
|
|
|
Literal newLiteral;
|
|
|
|
double val = stod(i);
|
|
|
|
newLiteral.val = val;
|
|
|
|
newInst.args.push_back(newLiteral);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Types::Bool:
|
|
|
|
{
|
|
|
|
Literal newLiteral;
|
|
|
|
bool val = (i == "true");
|
|
|
|
newLiteral.val = val;
|
|
|
|
newInst.args.push_back(newLiteral);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out.push_back(newInst);
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
main function
|
|
|
|
Takes input from the command line, reads the file specified by the user and
|
|
|
|
starts execution of the program. See also lexer function, parser function,
|
|
|
|
exec function.
|
|
|
|
*/
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
if (argc < 2) {
|
|
|
|
cout << "Usage: " << argv[0] << " (file)" << endl;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
ifstream file(argv[1]);
|
|
|
|
string lns;
|
|
|
|
string in;
|
|
|
|
while (getline(file, lns)) {
|
|
|
|
in += lns += "\n";
|
|
|
|
}
|
|
|
|
exec(parser(lexer(in)));
|
|
|
|
return 0;
|
|
|
|
}
|