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
2025-09-13 15:47:24 +10:00
along with this program . If not , see < https : //www.gnu.org/licenses/>.
2025-08-09 20:33:08 +10:00
*/
/*
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>
2025-08-30 10:06:25 +10:00
# include <stack>
2025-08-09 20:33:08 +10:00
# include <fstream>
2025-08-25 13:22:15 +10:00
# include <cstdlib>
# include <filesystem>
2025-08-09 20:33:08 +10:00
2025-08-25 17:35:16 +10:00
// 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
2025-08-09 20:33:08 +10:00
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 ,
2025-08-21 11:05:32 +10:00
Equal , Inequal , Greater , Lesser , Not ,
2025-08-30 10:40:59 +10:00
End , Set , Empty , Gettype , Exists ,
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-12 09:45:00 +10:00
Fun , Return , Endfun , Pusharg , Call , Local ,
2025-09-13 15:47:24 +10:00
Use , Extern , Error , Catch , Try , Exception
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-30 12:28:07 +10:00
Int , Double , String , Char , Bool , Value , Direct , Line , List , ListRef , Label , Type , Function
2025-08-09 20:33:08 +10:00
} ;
2025-08-30 12:28:07 +10:00
// Forward declaration of Literal for list
struct Literal ;
2025-08-09 20:33:08 +10:00
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
} ,
}
2025-09-13 15:47:24 +10:00
}
2025-08-10 13:31:28 +10:00
All elements in the list must be of the same type . See also Literal struct .
*/
struct List {
vector < Literal > val ;
} ;
2025-08-30 12:28:07 +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 , List > val ;
} ;
2025-08-11 14:57:45 +10:00
/*
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 ;
} ;
2025-08-12 09:45:00 +10:00
struct TypeRef {
Types type ;
} ;
struct FunctionRef {
string fnName ;
} ;
2025-08-11 14:57:45 +10:00
2025-08-31 15:04:27 +10:00
/*
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 ;
2025-08-10 13:31:28 +10:00
/*
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 ;
/*
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
*/
2025-08-31 15:04:27 +10:00
typedef variant < Literal , ValueRef , ListRef , FunctionRef , TypeRef , Direct , Line > argument ;
2025-08-09 20:33:08 +10:00
struct Instruction {
Instructions inst = Instructions : : Empty ;
2025-08-31 15:04:27 +10:00
vector < argument > args ;
bool isLabel = false ;
Label label ;
2025-08-09 20:33:08 +10:00
} ;
2025-08-22 13:52:26 +10:00
struct FnArg {
Direct ref ;
Types type ;
} ;
2025-08-13 09:40:03 +10:00
/*
Function struct
Contains information needed to run a Ground function .
*/
struct Function {
Types returnType ;
2025-08-22 13:52:26 +10:00
vector < FnArg > args ;
2025-08-13 09:40:03 +10:00
vector < Instruction > instructions ;
2025-08-31 15:04:27 +10:00
vector < Label > labels ;
2025-08-13 09:40:03 +10:00
} ;
2025-08-25 17:35:16 +10:00
// C-compatible enum and types for developing libraries for Ground in C
typedef enum {
GROUND_INT ,
2025-09-13 15:47:24 +10:00
GROUND_DOUBLE ,
2025-08-25 17:35:16 +10:00
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 ;
2025-08-13 09:40:03 +10:00
/*
functions map
Contains the code of functions and types of their values
*/
map < string , Function > functions ;
2025-08-15 11:35:58 +10:00
/*
fnArgs vector
2025-09-13 15:47:24 +10:00
Containst the arguments to be passed to a function
2025-08-15 11:35:58 +10:00
*/
vector < Literal > fnArgs ;
2025-08-13 18:31:54 +10:00
2025-08-25 17:35:16 +10:00
// 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 ;
2025-08-30 11:12:53 +10:00
// Libraries currently imported
vector < string > libraries ;
2025-08-25 17:35:16 +10:00
// 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 ;
}
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
2025-09-13 15:47:24 +10:00
user know what went wrong with the program .
2025-08-09 20:33:08 +10:00
*/
2025-09-13 15:47:24 +10:00
void error ( string in , string errCode = " syntaxError " , int exitc = 1 ) {
cout < < " \033 [31m " + errCode + " : \033 [39m " < < in < < endl ;
2025-08-09 20:33:08 +10:00
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 ;
}
2025-08-12 09:45:00 +10:00
bool isType ( string in ) {
if ( in . size ( ) > = 1 & & in [ 0 ] = = ' - ' ) {
string type = in . substr ( 1 ) ;
2025-09-13 15:47:24 +10:00
if ( type = = " string " | | type = = " char " | | type = = " bool " | | type = = " double " | | type = = " int " | | type = = " list " ) return true ;
2025-08-12 09:45:00 +10:00
else return false ;
} else return false ;
}
bool isFunction ( string in ) {
if ( in . size ( ) > = 1 & & in [ 0 ] = = ' ! ' ) return true ;
else return false ;
}
2025-08-09 20:33:08 +10:00
/*
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-12 09:45:00 +10:00
if ( isType ( in ) ) return Types : : Type ;
if ( isFunction ( in ) ) return Types : : Function ;
2025-08-09 20:33:08 +10:00
error ( " Could not determine type of \" " + in + " \" " ) ;
return Types : : Int ;
}
2025-08-22 13:52:26 +10:00
/*
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 ;
2025-09-13 15:47:24 +10:00
if ( holds_alternative < List > ( in . val ) ) return Types : : List ;
2025-08-22 13:52:26 +10:00
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 ;
}
2025-08-18 09:36:35 +10:00
bool processingFunction = false ;
string procFnName = " " ;
bool inFunction = false ;
2025-08-30 10:06:25 +10:00
// Stack of strings for keeping track of which thing we're importing
stack < string > importing ;
2025-08-25 13:22:15 +10:00
// Forward declaration for the call instruction and use instruction
2025-08-18 09:36:35 +10:00
Literal exec ( vector < Instruction > in ) ;
2025-08-25 13:22:15 +10:00
// 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 ) ;
2025-08-31 15:04:27 +10:00
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 ) ;
}
2025-08-09 20:33:08 +10:00
/*
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 .
*/
2025-08-24 16:30:42 +10:00
Literal exec ( vector < Instruction > in , bool executingFunction ) {
2025-08-09 20:33:08 +10:00
for ( int i = 0 ; i < in . size ( ) ; i + + ) {
Instruction l = in [ i ] ;
2025-08-13 09:40:03 +10:00
if ( processingFunction ) {
if ( l . inst = = Instructions : : Endfun ) {
processingFunction = false ;
procFnName = " " ;
continue ;
} else {
functions [ procFnName ] . instructions . push_back ( l ) ;
continue ;
}
}
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 ) {
2025-08-31 15:04:27 +10:00
if ( labelStack . top ( ) . find ( ln . label ) ! = labelStack . top ( ) . end ( ) ) {
2025-08-10 15:42:52 +10:00
Line newLine ;
2025-08-31 15:04:27 +10:00
newLine . lineNum = labelStack . top ( ) [ ln . label ] ;
2025-08-10 15:42:52 +10:00
l . args [ j ] = newLine ;
} else {
2025-08-30 10:50:19 +10:00
if ( l . inst ! = Instructions : : Exists ) error ( " Could not find label " + ln . label ) ;
2025-08-10 15:42:52 +10:00
}
}
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 {
2025-09-13 15:47:24 +10:00
error ( " Couldn't print that " , " printError " ) ;
}
} else if ( holds_alternative < ListRef > ( l . args [ 0 ] ) ) {
if ( variables . find ( get < ListRef > ( l . args [ 0 ] ) . listName ) ! = variables . end ( ) ) {
List list = get < List > ( variables [ get < ListRef > ( l . args [ 0 ] ) . listName ] . val ) ;
cout < < " [ " ;
for ( int l = 0 ; l < list . val . size ( ) ; l + + ) {
if ( holds_alternative < string > ( list . val [ l ] . val ) ) {
cout < < ' " ' < < get < string > ( list . val [ l ] . val ) < < ' " ' ;
}
else if ( holds_alternative < int > ( list . val [ l ] . val ) ) {
cout < < get < int > ( list . val [ l ] . val ) ;
}
else if ( holds_alternative < double > ( list . val [ l ] . val ) ) {
cout < < get < double > ( list . val [ l ] . val ) ;
}
else if ( holds_alternative < bool > ( list . val [ l ] . val ) ) {
if ( get < bool > ( list . val [ l ] . val ) = = true ) {
cout < < " true " ;
} else {
cout < < " false " ;
}
}
else if ( holds_alternative < char > ( list . val [ l ] . val ) ) {
cout < < ' \' ' < < get < char > ( list . val [ l ] . val ) < < ' \' ' ;
}
else {
error ( " Couldn't print that " , " printError " ) ;
}
if ( l ! = list . val . size ( ) - 1 ) {
cout < < " , " ;
}
}
cout < < " ] " ;
} else {
error ( " Couldn't find list named " + get < ListRef > ( l . args [ 0 ] ) . listName ) ;
2025-08-09 20:33:08 +10:00
}
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Couldn't print that " , " printError " ) ;
}
} else if ( holds_alternative < ListRef > ( l . args [ 0 ] ) ) {
if ( variables . find ( get < ListRef > ( l . args [ 0 ] ) . listName ) ! = variables . end ( ) ) {
List list = get < List > ( variables [ get < ListRef > ( l . args [ 0 ] ) . listName ] . val ) ;
cout < < " [ " ;
for ( int l = 0 ; l < list . val . size ( ) ; l + + ) {
if ( holds_alternative < string > ( list . val [ l ] . val ) ) {
cout < < ' " ' < < get < string > ( list . val [ l ] . val ) < < ' " ' ;
}
else if ( holds_alternative < int > ( list . val [ l ] . val ) ) {
cout < < get < int > ( list . val [ l ] . val ) ;
}
else if ( holds_alternative < double > ( list . val [ l ] . val ) ) {
cout < < get < double > ( list . val [ l ] . val ) ;
}
else if ( holds_alternative < bool > ( list . val [ l ] . val ) ) {
if ( get < bool > ( list . val [ l ] . val ) = = true ) {
cout < < " true " ;
} else {
cout < < " false " ;
}
}
else if ( holds_alternative < char > ( list . val [ l ] . val ) ) {
cout < < ' \' ' < < get < char > ( list . val [ l ] . val ) < < ' \' ' ;
}
else {
error ( " Couldn't print that " , " printError " ) ;
}
if ( l ! = list . val . size ( ) - 1 ) {
cout < < " , " ;
}
}
cout < < " ] " < < endl ;
} else {
error ( " Couldn't find list named " + get < ListRef > ( l . args [ 0 ] ) . listName ) ;
2025-08-09 20:33:08 +10:00
}
} else {
2025-09-13 15:47:24 +10:00
error ( " Argument of stdlnout must be a value (literal or a value reference) or a list reference " ) ;
2025-08-09 20:33:08 +10:00
}
break ;
2025-09-12 11:57:45 +10:00
/*
error instruction
This instruction outputs a custom error message .
*/
case Instructions : : Error :
2025-09-13 15:47:24 +10:00
if ( l . args . size ( ) < 2 ) {
error ( " Could not find arguments for Error inbuilt " ) ;
2025-09-12 11:57:45 +10:00
}
if ( holds_alternative < Literal > ( l . args [ 0 ] ) ) {
2025-09-13 15:47:24 +10:00
if ( holds_alternative < string > ( get < Literal > ( l . args [ 0 ] ) . val ) & & holds_alternative < string > ( get < Literal > ( l . args [ 1 ] ) . val ) ) {
error ( get < string > ( get < Literal > ( l . args [ 0 ] ) . val ) , get < string > ( get < Literal > ( l . args [ 1 ] ) . val ) ) ;
2025-09-12 11:57:45 +10:00
} else {
error ( " Argument of error must be a string " ) ;
}
} else {
2025-09-13 15:47:24 +10:00
error ( " Argument of error must be a string " ) ;
2025-09-12 11:57:45 +10:00
}
break ;
2025-09-13 15:47:24 +10:00
/*
catch instruction
This instruction ensures that errors in programs are caught and dealt with appropriately .
*/
case Instructions : : Catch :
if ( l . args . size ( ) < 2 ) {
error ( " Could not find all arguments for Catch inbuilt " ) ;
}
if ( holds_alternative < Literal > ( l . args [ 0 ] ) ) {
if ( holds_alternative < string > ( get < Literal > ( l . args [ 0 ] ) . val ) ) {
2025-09-20 10:35:53 +10:00
2025-09-13 15:47:24 +10:00
}
}
2025-08-09 20:33:08 +10:00
/*
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
Literal varContents ;
if ( holds_alternative < Direct > ( l . args [ 0 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 0 ] ) ;
2025-08-09 20:33:08 +10:00
} 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) " ) ;
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = varContents ;
2025-08-09 20:33:08 +10:00
}
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 :
2025-08-30 14:20:34 +10:00
if ( l . args . size ( ) < 1 ) {
2025-08-10 13:31:28 +10:00
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 ;
2025-08-31 15:04:27 +10:00
for ( argument k : l . args ) {
2025-08-10 13:31:28 +10:00
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 ;
}
}
2025-08-30 12:28:07 +10:00
variables [ listName ] . val = listContents ;
2025-08-10 13:31:28 +10:00
}
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 ) ;
2025-09-13 15:47:24 +10:00
} else {
2025-08-10 13:31:28 +10:00
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 " ) ;
}
2025-08-30 12:28:07 +10:00
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 {
2025-09-13 15:47:24 +10:00
error ( " Index " + to_string ( ref ) + " out of range of list " + listref . listName , " rangeError " ) ;
2025-08-30 12:28:07 +10:00
}
2025-08-10 13:31:28 +10:00
} else {
2025-08-30 14:20:34 +10:00
error ( " Found a normal variable in place of a list " ) ;
2025-08-10 13:31:28 +10:00
}
} else {
2025-08-30 14:20:34 +10:00
error ( " Unknown list: " + listref . listName ) ;
2025-08-10 13:31:28 +10:00
}
}
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 {
2025-08-25 11:22:02 +10:00
error ( " First argument of getstrcharat must be a string literal " ) ;
2025-08-11 10:07:05 +10:00
}
} else {
2025-08-25 11:22:02 +10:00
error ( " First argument of getstrcharat must be a string literal " ) ;
2025-08-11 10:07:05 +10:00
}
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 ) ;
2025-09-13 15:47:24 +10:00
} else {
2025-08-25 11:22:02 +10:00
error ( " Second argument of getstrcharat must be an integer literal " ) ;
2025-08-11 10:07:05 +10:00
}
} else {
2025-08-25 11:22:02 +10:00
error ( " Second argument of getstrcharat must be an integer literal " ) ;
2025-08-11 10:07:05 +10:00
}
if ( holds_alternative < Direct > ( l . args [ 2 ] ) ) {
var = get < Direct > ( l . args [ 2 ] ) ;
} else {
2025-08-25 11:22:02 +10:00
error ( " Third argument of getstrcharat must be a direct reference " ) ;
2025-08-11 10:07:05 +10:00
}
if ( instr . size ( ) > ref ) {
Literal newLit ;
newLit . val = instr [ ref ] ;
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( var . varName ) > 0 ;
2025-08-11 10:07:05 +10:00
variables [ var . varName ] = newLit ;
} else {
2025-09-13 15:47:24 +10:00
error ( " Index " + to_string ( ref ) + " out of range of string " + instr , " rangeError " ) ;
2025-08-11 10:07:05 +10:00
}
}
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 ) ;
2025-09-13 15:47:24 +10:00
} else {
2025-08-10 16:08:56 +10:00
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 " ) ;
}
2025-08-30 12:28:07 +10:00
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 {
2025-09-13 15:47:24 +10:00
error ( " Index " + to_string ( ref ) + " out of range of list " + listref . listName , " rangeError " ) ;
2025-08-30 12:28:07 +10:00
}
2025-08-10 16:08:56 +10:00
}
} 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 {
2025-08-31 15:04:27 +10:00
error ( " First argument of listappend must be a list reference " ) ;
2025-08-11 08:57:45 +10:00
}
if ( holds_alternative < Literal > ( l . args [ 1 ] ) ) {
value = get < Literal > ( l . args [ 1 ] ) ;
} else {
error ( " Second argument of listappend must be a direct reference " ) ;
}
2025-08-30 12:28:07 +10:00
if ( variables . find ( listref . listName ) ! = variables . end ( ) ) {
2025-08-30 14:20:34 +10:00
if ( ! holds_alternative < List > ( variables [ listref . listName ] . val ) ) {
error ( " Variable " + listref . listName + " is not a list " ) ;
}
2025-08-30 12:28:07 +10:00
List tmpList = get < List > ( variables [ listref . listName ] . val ) ;
tmpList . val . push_back ( value ) ;
variables [ listref . listName ] . val = tmpList ;
2025-08-11 08:57:45 +10:00
} 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 ;
2025-09-13 15:47:24 +10:00
2025-08-10 13:31:28 +10:00
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 ;
2025-08-30 12:28:07 +10:00
if ( variables . find ( ref . listName ) ! = variables . end ( ) ) {
newLit . val = int ( get < List > ( variables [ ref . listName ] . val ) . val . size ( ) ) ;
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( var . varName ) > 0 ;
2025-08-10 13:31:28 +10:00
variables [ var . varName ] = newLit ;
} else {
error ( " Couldn't find the list " + ref . listName ) ;
}
2025-09-13 15:47:24 +10:00
2025-08-10 13:31:28 +10:00
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 ;
2025-09-13 15:47:24 +10:00
2025-08-11 10:07:05 +10:00
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 ( ) ) ;
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( var . varName ) > 0 ;
2025-08-11 10:07:05 +10:00
variables [ var . varName ] = newLit ;
2025-09-13 15:47:24 +10:00
2025-08-11 10:07:05 +10:00
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 ;
2025-09-13 15:47:24 +10:00
2025-08-11 14:12:25 +10:00
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 ) ;
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( ref . varName ) > 0 ;
2025-08-11 14:12:25 +10:00
variables [ ref . varName ] = newLit ;
} else {
2025-09-13 15:47:24 +10:00
error ( " Cannot convert the value " + toConv + " to an int " , " conversionError " ) ;
2025-08-11 14:12:25 +10:00
}
}
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 ;
2025-09-13 15:47:24 +10:00
2025-08-11 14:12:25 +10:00
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 ) ;
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( ref . varName ) > 0 ;
2025-08-11 14:12:25 +10:00
variables [ ref . varName ] = newLit ;
} else {
2025-09-13 15:47:24 +10:00
error ( " Cannot convert the value " + toConv + " to a decimal " , " conversionError " ) ;
2025-08-11 14:12:25 +10:00
}
}
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 ;
2025-09-13 15:47:24 +10:00
2025-08-11 14:12:25 +10:00
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 " ;
}
2025-09-13 15:47:24 +10:00
}
2025-08-11 14:12:25 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( ref . varName ) > 0 ;
2025-08-11 14:12:25 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
Direct varRef = get < Direct > ( l . args [ 0 ] ) ;
2025-08-09 20:33:08 +10:00
string userIn ;
getline ( cin , userIn ) ;
Literal userLit ;
userLit . val = userIn ;
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = userLit ;
2025-08-09 20:33:08 +10:00
} 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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot add those two values " , " mathError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot subtract those two values " , " mathError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot multiply those two values " , " mathError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 ) {
2025-09-13 15:47:24 +10:00
error ( " Division by zero is not allowed " , " divisionByZeroError " ) ;
2025-08-09 20:33:08 +10:00
}
}
if ( holds_alternative < double > ( right . val ) ) {
if ( get < double > ( right . val ) = = 0 ) {
2025-09-13 15:47:24 +10:00
error ( " Division by zero is not allowed " , " divisionByZeroError " ) ;
2025-08-09 20:33:08 +10:00
}
}
// 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot divide those two values " , " mathError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot equal those two values " , " comparisonError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot inequal those two values " , " comparisonError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
break ;
2025-08-21 11:05:32 +10:00
/*
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 ;
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-09-13 15:47:24 +10:00
2025-08-21 11:05:32 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 1 ] ) ;
2025-08-21 11:05:32 +10:00
} else {
error ( " Second argument of not must be a direct reference " ) ;
}
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = boolean ;
2025-08-21 11:05:32 +10:00
}
break ;
2025-08-09 20:33:08 +10:00
/*
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot greater those two values " , " comparisonError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 " ) ;
}
{
2025-08-24 15:08:07 +10:00
Direct varRef ;
2025-08-09 20:33:08 +10:00
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 ] ) ) {
2025-08-24 15:08:07 +10:00
varRef = get < Direct > ( l . args [ 2 ] ) ;
2025-08-09 20:33:08 +10:00
} 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 {
2025-09-13 15:47:24 +10:00
error ( " Cannot lesser those two values " , " comparisonError " ) ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-24 15:08:07 +10:00
bool existed = variables . count ( varRef . varName ) > 0 ;
variables [ varRef . varName ] = final ;
2025-08-09 20:33:08 +10:00
}
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 ] ) ) {
2025-08-31 15:04:27 +10:00
i = get < Line > ( l . args [ 0 ] ) . lineNum - 1 ;
2025-08-09 20:33:08 +10:00
} 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 ] ) ) {
2025-08-31 15:04:27 +10:00
i = get < Line > ( l . args [ 1 ] ) . lineNum - 1 ;
2025-08-09 20:33:08 +10:00
} else {
error ( " Second argument of if must be a line reference " ) ;
}
}
}
break ;
2025-08-11 14:57:45 +10:00
/*
2025-08-13 09:40:03 +10:00
end instruction
2025-08-11 14:57:45 +10:00
Ends execution of the code , eith the status code provided .
*/
2025-08-09 20:33:08 +10:00
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 " ) ;
}
2025-08-30 10:24:31 +10:00
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 :
2025-09-13 15:47:24 +10:00
error ( " Could not get type?? This should never be reached. Please report this issue " , " undefinedError " ) ;
2025-08-30 10:24:31 +10:00
break ;
}
2025-09-13 15:47:24 +10:00
2025-08-30 10:24:31 +10:00
}
2025-08-30 10:40:59 +10:00
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 ] ) ;
2025-08-30 10:50:19 +10:00
} else {
error ( " Second argument of exists must be a direct reference " ) ;
2025-08-30 10:40:59 +10:00
}
2025-08-30 10:50:19 +10:00
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 ] ) ) {
2025-08-30 12:28:07 +10:00
if ( variables . find ( get < ListRef > ( l . args [ 0 ] ) . listName ) ! = variables . end ( ) & & holds_alternative < List > ( variables [ get < ListRef > ( l . args [ 0 ] ) . listName ] . val ) ) {
2025-08-30 10:50:19 +10:00
exists = true ;
}
} else if ( holds_alternative < Line > ( l . args [ 0 ] ) ) {
Line line = get < Line > ( l . args [ 0 ] ) ;
if ( line . isLabel ) {
2025-08-31 15:04:27 +10:00
if ( labelStack . top ( ) . find ( line . label ) ! = labelStack . top ( ) . end ( ) ) {
2025-08-30 10:50:19 +10:00
exists = true ;
}
} else {
if ( line . lineNum > 0 & & line . lineNum < = in . size ( ) ) {
exists = true ;
}
}
2025-08-30 10:40:59 +10:00
} else {
2025-08-30 10:50:19 +10:00
error ( " First argument of exists must be a direct, list, or line reference " ) ;
2025-08-30 10:40:59 +10:00
}
2025-08-30 10:50:19 +10:00
variables [ ref2 . varName ] . val = exists ;
2025-08-30 10:40:59 +10:00
}
2025-08-09 20:33:08 +10:00
break ;
2025-08-13 09:40:03 +10:00
/*
fun instruction
Allows functions to be defined .
*/
2025-08-11 14:57:45 +10:00
case Instructions : : Fun :
2025-08-24 16:30:42 +10:00
if ( l . args . size ( ) < 2 ) {
2025-08-13 09:40:03 +10:00
error ( " Could not find all arguments required for Fun inbuilt " ) ;
}
{
Function newFunction ;
2025-09-13 15:47:24 +10:00
2025-08-13 09:40:03 +10:00
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 ;
2025-09-13 15:47:24 +10:00
2025-08-13 09:40:03 +10:00
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 " ) ;
}
2025-08-30 10:06:25 +10:00
if ( importing . size ( ) > 0 ) {
fnName = importing . top ( ) + " : " + fnName ;
}
2025-08-22 13:52:26 +10:00
// 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 ;
2025-09-13 15:47:24 +10:00
2025-08-22 13:52:26 +10:00
// Get type
if ( holds_alternative < TypeRef > ( l . args [ m ] ) ) {
newArg . type = get < TypeRef > ( l . args [ m ] ) . type ;
2025-08-13 09:40:03 +10:00
} else {
2025-08-22 13:52:26 +10:00
error ( " Expected type reference in function argument definition " ) ;
2025-08-13 09:40:03 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-22 13:52:26 +10:00
// 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 " ) ;
}
2025-09-13 15:47:24 +10:00
2025-08-22 13:52:26 +10:00
newFunction . args . push_back ( newArg ) ;
2025-08-13 09:40:03 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-13 09:40:03 +10:00
functions [ fnName ] = newFunction ;
processingFunction = true ;
procFnName = fnName ;
}
2025-08-11 14:57:45 +10:00
break ;
2025-08-13 09:40:03 +10:00
/*
return instruction
Exits a function .
*/
2025-08-11 14:57:45 +10:00
case Instructions : : Return :
2025-08-13 18:31:54 +10:00
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 ] ) ;
2025-08-30 13:05:28 +10:00
} else if ( holds_alternative < ListRef > ( l . args [ 0 ] ) ) {
return variables [ get < ListRef > ( l . args [ 0 ] ) . listName ] ;
2025-08-13 18:31:54 +10:00
} else {
error ( " First argument of return must be a literal value/value reference " ) ;
}
2025-08-11 14:57:45 +10:00
break ;
2025-08-13 09:40:03 +10:00
/*
endfun instruction
2025-09-13 15:47:24 +10:00
This should not be reached during normal execution .
2025-08-13 09:40:03 +10:00
*/
2025-08-11 14:57:45 +10:00
case Instructions : : Endfun :
2025-08-13 09:40:03 +10:00
error ( " No function is being defined. Cannot end function declaration here " ) ;
2025-08-11 14:57:45 +10:00
break ;
2025-08-18 09:36:35 +10:00
/*
pusharg instruction
This instruction makes new arguments avaliable for functions .
*/
2025-08-11 14:57:45 +10:00
case Instructions : : Pusharg :
2025-08-15 11:35:58 +10:00
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 ] ) ) ;
2025-08-30 13:05:28 +10:00
} else if ( holds_alternative < ListRef > ( l . args [ 0 ] ) ) {
fnArgs . push_back ( variables [ get < ListRef > ( l . args [ 0 ] ) . listName ] ) ;
2025-08-15 11:35:58 +10:00
} else {
2025-08-30 13:05:28 +10:00
error ( " First argument of pusharg must be a literal or list reference " ) ;
2025-08-15 11:35:58 +10:00
}
2025-08-11 14:57:45 +10:00
break ;
2025-08-18 09:36:35 +10:00
/*
call instruction
This instruction calls a function , and makes the arguments avaliable for it .
*/
2025-08-11 14:57:45 +10:00
case Instructions : : Call :
2025-08-18 09:36:35 +10:00
{
if ( l . args . size ( ) < 2 ) {
error ( " Could not find all arguments required for Call inbuilt " ) ;
}
2025-09-13 15:47:24 +10:00
2025-08-18 09:36:35 +10:00
FunctionRef ref ;
2025-08-30 13:05:28 +10:00
string returnRef ;
bool expectList = true ;
2025-09-13 15:47:24 +10:00
2025-08-18 09:36:35 +10:00
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 ] ) ) {
2025-08-30 13:05:28 +10:00
returnRef = get < Direct > ( l . args [ 1 ] ) . varName ;
} else if ( holds_alternative < ListRef > ( l . args [ 1 ] ) ) {
returnRef = get < ListRef > ( l . args [ 1 ] ) . listName ;
expectList = true ;
2025-08-18 09:36:35 +10:00
} else {
2025-08-30 13:05:28 +10:00
error ( " Second argument of call must be a direct reference or list reference " ) ;
2025-08-18 09:36:35 +10:00
}
2025-08-25 17:35:16 +10:00
// 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 ] ;
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
// Convert arguments
vector < GroundValue > gvArgs ;
for ( const Literal & arg : fnArgs ) {
gvArgs . push_back ( literalToGroundValue ( arg ) ) ;
}
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
// Call function
GroundValue result = extFunc ( gvArgs . data ( ) , gvArgs . size ( ) ) ;
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
// Convert result back
Literal resultLit = groundValueToLiteral ( result ) ;
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
// Clear arguments and store result
fnArgs . clear ( ) ;
2025-08-30 13:05:28 +10:00
variables [ returnRef ] = resultLit ;
2025-08-25 17:35:16 +10:00
break ;
}
2025-08-22 13:52:26 +10:00
// 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 ( ) ) {
2025-09-13 15:47:24 +10:00
error ( " Function " + ref . fnName + " expects " +
to_string ( functions [ ref . fnName ] . args . size ( ) ) +
2025-08-22 13:52:26 +10:00
" arguments, got " + to_string ( fnArgs . size ( ) ) ) ;
}
2025-08-25 13:35:22 +10:00
// Create backup of variables to be restored
map < string , Literal > scopeBackup = variables ;
2025-08-22 13:52:26 +10:00
// Create function arguments with type checking
for ( int m = 0 ; m < functions [ ref . fnName ] . args . size ( ) ; m + + ) {
FnArg arg = functions [ ref . fnName ] . args [ m ] ;
2025-09-13 15:47:24 +10:00
2025-08-22 13:52:26 +10:00
// Type checking - now with error reporting
if ( arg . type ! = getLitType ( fnArgs [ m ] ) ) {
2025-09-13 15:47:24 +10:00
error ( " Function " + ref . fnName + " argument " + to_string ( m + 1 ) +
" type mismatch. Expected type does not match provided argument type. " , " typeError " ) ;
2025-08-22 13:52:26 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-22 13:52:26 +10:00
// Create the variable
2025-09-13 15:47:24 +10:00
variables [ arg . ref . varName ] = fnArgs [ m ] ;
2025-08-22 13:52:26 +10:00
}
2025-09-13 15:47:24 +10:00
2025-08-30 13:39:51 +10:00
// Clear function arguments for next call
fnArgs . clear ( ) ;
2025-08-22 13:52:26 +10:00
2025-08-31 15:04:27 +10:00
// Process labels
preProcessLabels ( functions [ ref . fnName ] . instructions ) ;
2025-08-22 13:52:26 +10:00
// Call the function
2025-08-24 16:30:42 +10:00
Literal retVal = exec ( functions [ ref . fnName ] . instructions , true ) ;
2025-08-22 13:52:26 +10:00
2025-08-25 13:35:22 +10:00
// Restore scope
variables = scopeBackup ;
2025-08-31 15:04:27 +10:00
labelStack . pop ( ) ;
2025-08-24 15:08:07 +10:00
// Now, assign the return value in the current scope.
2025-08-30 13:05:28 +10:00
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 ;
}
}
2025-08-18 09:36:35 +10:00
}
2025-08-11 14:57:45 +10:00
break ;
case Instructions : : Use :
2025-08-25 13:22:15 +10:00
if ( l . args . size ( ) < 1 ) {
error ( " Could not find all arguments for Use inbuilt " ) ;
}
{
string useName ;
2025-09-13 15:47:24 +10:00
2025-08-25 13:22:15 +10:00
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 " ) ;
}
2025-08-30 11:12:53 +10:00
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 ;
2025-09-20 10:35:53 +10:00
string groundLibsDir = string ( getenv ( " GROUND_LIBS " ) ) + " / " ;
2025-08-25 13:22:15 +10:00
if ( filesystem : : exists ( useName ) ) {
2025-09-13 15:47:24 +10:00
// no need to do anything here
2025-09-20 10:35:53 +10:00
} else if ( groundLibsDir ! = " " & & filesystem : : exists ( groundLibsDir + useName ) ) {
useName = groundLibsDir + useName ;
2025-08-25 13:22:15 +10:00
} else {
2025-09-13 15:47:24 +10:00
error ( " Could not find external Ground library in $GROUND_LIBS (currently set to " + groundLibsDir + " ) or current directory. " , " libraryError " ) ;
2025-08-25 13:22:15 +10:00
}
ifstream file ( useName ) ;
string lns ;
string in ;
while ( getline ( file , lns ) ) {
in + = lns + = " \n " ;
}
2025-08-30 10:06:25 +10:00
importing . push ( get < string > ( get < Literal > ( l . args [ 0 ] ) . val ) ) ;
2025-08-31 15:04:27 +10:00
vector < Instruction > parsed = parser ( lexer ( in ) ) ;
preProcessLabels ( parsed ) ;
Literal ret = exec ( parsed , false ) ;
2025-08-30 10:06:25 +10:00
importing . pop ( ) ;
2025-08-30 11:12:53 +10:00
libraries . push_back ( libName ) ;
2025-08-30 10:06:25 +10:00
}
2025-08-11 14:57:45 +10:00
break ;
case Instructions : : Extern :
2025-08-25 17:35:16 +10:00
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 " ) ;
}
2025-08-30 11:12:53 +10:00
bool imported = false ;
for ( string lib : libraries ) {
if ( lib = = libName ) {
imported = true ;
break ;
}
}
if ( imported ) break ;
2025-08-25 17:35:16 +10:00
// 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 ;
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
// 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 ;
}
}
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
if ( ! found ) {
2025-09-13 15:47:24 +10:00
error ( " Could not find external library: " + fullLibName +
" (searched current directory and GROUND_LIBS) " , " libraryError " ) ;
2025-08-25 17:35:16 +10:00
}
// Try to open the library
void * handle = DLOPEN ( libPath . c_str ( ) ) ;
if ( ! handle ) {
2025-09-13 15:47:24 +10:00
error ( " Failed to load library " + libPath + " : " + string ( DLERROR ( ) ) , " libraryError " ) ;
2025-08-25 17:35:16 +10:00
}
// 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 ) {
2025-09-13 15:47:24 +10:00
error ( " Library " + libName + " is not Ground-compatible (missing required functions: ground_get_functions or ground_get_function) " , " libraryError " ) ;
2025-08-25 17:35:16 +10:00
}
// 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 " ) ;
}
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
int functionCount = 0 ;
for ( int i = 0 ; functionNames [ i ] ! = nullptr ; i + + ) {
void * funcPtr = getFunction ( functionNames [ i ] ) ;
if ( funcPtr ) {
2025-08-30 10:06:25 +10:00
externalFunctions [ libName + " : " + string ( functionNames [ i ] ) ] = funcPtr ;
2025-08-25 17:35:16 +10:00
functionCount + + ;
} else {
2025-09-13 15:47:24 +10:00
error ( " Failed to get function pointer for: " + string ( functionNames [ i ] ) , " libraryError " ) ;
2025-08-25 17:35:16 +10:00
}
}
2025-09-13 15:47:24 +10:00
2025-08-25 17:35:16 +10:00
if ( functionCount = = 0 ) {
2025-09-13 15:47:24 +10:00
error ( " No functions were loaded from library: " + libName , " libraryError " ) ;
2025-08-25 17:35:16 +10:00
}
}
2025-08-11 14:57:45 +10:00
break ;
2025-08-09 20:33:08 +10:00
default :
cout < < " Still to be implemented " < < endl ;
break ;
}
}
2025-08-30 16:28:27 +10:00
if ( executingFunction ) error ( " Reached end of function and no value returned " ) ;
2025-08-13 18:31:54 +10:00
Literal retLiteral ;
return retLiteral ;
2025-08-09 20:33:08 +10:00
}
2025-09-01 13:10:46 +10:00
string interpretEscapeSequences ( string input ) {
string output ;
for ( size_t i = 0 ; i < input . length ( ) ; + + i ) {
if ( input [ i ] = = ' \\ ' & & i + 1 < input . length ( ) ) {
char next = input [ i + 1 ] ;
switch ( next ) {
case ' n ' : output + = ' \n ' ; break ;
case ' t ' : output + = ' \t ' ; break ;
case ' r ' : output + = ' \r ' ; break ;
case ' b ' : output + = ' \b ' ; break ;
case ' f ' : output + = ' \f ' ; break ;
case ' a ' : output + = ' \a ' ; break ;
case ' v ' : output + = ' \v ' ; break ;
case ' \\ ' : output + = ' \\ ' ; break ;
case ' \' ' : output + = ' \' ' ; break ;
case ' \" ' : output + = ' \" ' ; break ;
case ' 0 ' : output + = ' \0 ' ; break ;
default :
output + = ' \\ ' ;
output + = next ;
break ;
}
+ + i ;
} else {
output + = input [ i ] ;
}
}
return output ;
}
2025-08-09 20:33:08 +10:00
/*
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 ;
2025-09-13 15:47:24 +10:00
2025-08-09 20:33:08 +10:00
for ( char i : in ) {
switch ( i ) {
case ' " ' :
if ( ! isComment ) {
2025-08-18 13:38:26 +10:00
if ( procChar ) {
buf . push_back ( i ) ;
} else {
procString = ! procString ;
buf . push_back ( i ) ;
}
2025-08-09 20:33:08 +10:00
}
break ;
case ' \' ' :
if ( ! isComment ) {
2025-08-18 13:38:26 +10:00
if ( procString ) {
buf . push_back ( i ) ;
} else {
procChar = ! procChar ;
buf . push_back ( i ) ;
}
2025-08-09 20:33:08 +10:00
}
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
2025-08-31 15:04:27 +10:00
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 .
2025-08-09 20:33:08 +10:00
*/
vector < Instruction > parser ( vector < vector < string > > in ) {
vector < Instruction > out ;
2025-08-31 15:04:27 +10:00
int lineNum = 0 ;
2025-09-13 15:47:24 +10:00
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 ;
2025-09-13 15:47:24 +10:00
2025-08-09 20:33:08 +10:00
for ( string i : lineTokens ) {
if ( firstInst ) {
firstInst = false ;
2025-08-10 15:42:52 +10:00
if ( isLabel ( i ) ) {
2025-08-31 15:04:27 +10:00
newInst . isLabel = true ;
newInst . label . id = i . substr ( 1 ) ;
2025-08-10 15:42:52 +10:00
}
2025-09-13 15:47:24 +10:00
else if ( isFunction ( i ) ) {
newInst . inst = Instructions : : Call ;
FunctionRef newFnRef ;
newFnRef . fnName = i . substr ( 1 ) ;
newInst . args . push_back ( newFnRef ) ;
}
2025-08-10 15:42:52 +10:00
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 ;
2025-09-12 11:57:45 +10:00
else if ( i = = " error " ) newInst . inst = Instructions : : Error ;
2025-09-13 15:47:24 +10:00
else if ( i = = " try " ) newInst . inst = Instructions : : Try ;
else if ( i = = " catch " ) newInst . inst = Instructions : : Catch ;
else if ( i = = " exception " ) newInst . inst = Instructions : : Exception ;
2025-08-09 20:33:08 +10:00
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 ;
2025-08-24 14:41:34 +10:00
else if ( i = = " not " ) newInst . inst = Instructions : : Not ;
2025-08-09 20:33:08 +10:00
else if ( i = = " end " ) newInst . inst = Instructions : : End ;
else if ( i = = " set " ) newInst . inst = Instructions : : Set ;
2025-08-30 10:24:31 +10:00
else if ( i = = " gettype " ) newInst . inst = Instructions : : Gettype ;
2025-08-30 10:40:59 +10:00
else if ( i = = " exists " ) newInst . inst = Instructions : : Exists ;
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-31 15:04:27 +10:00
else if ( i = = " fun " ) newInst . inst = Instructions : : Fun ;
2025-08-11 14:57:45 +10:00
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 ;
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-12 09:45:00 +10:00
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 ;
2025-08-30 12:28:07 +10:00
else if ( type = = " list " ) newType . type = Types : : List ;
2025-09-13 15:47:24 +10:00
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. " , " undefinedError " ) ;
2025-08-13 09:40:03 +10:00
newInst . args . push_back ( newType ) ;
2025-08-12 09:45:00 +10:00
}
break ;
case Types : : Function :
{
FunctionRef newFunction ;
newFunction . fnName = i . substr ( 1 ) ;
2025-09-13 15:47:24 +10:00
newInst . args . push_back ( newFunction ) ;
2025-08-12 09:45:00 +10:00
}
2025-08-13 09:40:03 +10:00
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 ;
2025-09-01 13:10:46 +10:00
string str = interpretEscapeSequences ( i . substr ( 1 , i . size ( ) - 2 ) ) ;
2025-08-09 20:33:08 +10:00
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 ;
2025-08-30 12:28:07 +10:00
default :
2025-09-13 15:47:24 +10:00
error ( " This type should not be obtained in normal execution " , " undefinedError " ) ;
2025-08-30 12:28:07 +10:00
break ;
2025-08-09 20:33:08 +10:00
}
2025-09-13 15:47:24 +10:00
}
2025-08-09 20:33:08 +10:00
}
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 ) ;
}
2025-08-25 19:13:00 +10:00
// Get args
List argsList ;
for ( int i = 2 ; i < argc ; i + + ) {
Literal lit ;
lit . val = argv [ i ] ;
argsList . val . push_back ( lit ) ;
}
2025-08-30 12:28:07 +10:00
variables [ " args " ] . val = argsList ;
2025-09-13 15:47:24 +10:00
2025-08-09 20:33:08 +10:00
ifstream file ( argv [ 1 ] ) ;
string lns ;
string in ;
while ( getline ( file , lns ) ) {
in + = lns + = " \n " ;
}
2025-08-31 15:04:27 +10:00
vector < Instruction > parsed = parser ( lexer ( in ) ) ;
preProcessLabels ( parsed ) ;
Literal ret = exec ( parsed , false ) ;
2025-08-13 18:31:54 +10:00
if ( holds_alternative < int > ( ret . val ) ) {
return get < int > ( ret . val ) ;
} else {
return 0 ;
}
2025-08-09 20:33:08 +10:00
return 0 ;
}