20 Commits

Author SHA1 Message Date
c952be1fe6 Command line arguments 2025-08-25 19:13:00 +10:00
1c5ca8d201 Simple file and request libraries 2025-08-25 18:29:45 +10:00
e56e6de911 Experimental external library support 2025-08-25 17:35:16 +10:00
38b17e7db5 Add library guide 2025-08-25 14:10:14 +10:00
e74e5ea548 Update syntax 2025-08-25 13:51:22 +10:00
f5140e3833 Delete docs/writing-a-program 2025-08-25 13:36:04 +10:00
c01dd470e1 Update docs/writing-a-program.md 2025-08-25 13:35:56 +10:00
16660c6a8d More reliable scoping 2025-08-25 13:35:22 +10:00
5bd8519517 External library support 2025-08-25 13:22:15 +10:00
2f706e2285 Update lines of code in readme 2025-08-25 11:29:43 +10:00
db99b9ac9f Fix getstrcharat error message 2025-08-25 11:22:02 +10:00
e906734aca Fix function jumping bug (I think) 2025-08-24 20:22:52 +10:00
8da5a2bf93 Experimental function jumping 2025-08-24 16:30:42 +10:00
e9600d8500 Scoping 2025-08-24 15:08:07 +10:00
1c0dfcc4b7 Fix not 2025-08-24 14:41:34 +10:00
f7f3972248 Function arguments, start of scoping 2025-08-22 13:52:26 +10:00
50d83aa228 "not" instruction 2025-08-21 11:05:32 +10:00
14758df1ab Example calculator 2025-08-21 08:43:22 +10:00
b19b4123d8 Parser string and character fix 2025-08-18 13:38:26 +10:00
28a9e389fa Basic function calling support 2025-08-18 09:36:35 +10:00
13 changed files with 1003 additions and 98 deletions

View File

@@ -8,7 +8,7 @@ Ground is an interpreter which processes and interprets Ground instructions. It
* **Simple syntax:** Ground doesn't have very many features, and that's intentional. It makes Ground easy to learn, and keeps it speedy. * **Simple syntax:** Ground doesn't have very many features, and that's intentional. It makes Ground easy to learn, and keeps it speedy.
* **Super speed:** Ground code is faster than Python and JavaScript, and nearly as fast as C++ and Rust, while still being interpreted. (Tested using tests/to1000.grnd) * **Super speed:** Ground code is faster than Python and JavaScript, and nearly as fast as C++ and Rust, while still being interpreted. (Tested using tests/to1000.grnd)
* **Tiny interpreter:** Ground contains 1154 lines of code (and 320 lines of comments) at the time of writing, and compiles in seconds. * **Tiny interpreter:** Ground contains 1482 lines of code (and 382 lines of comments) at the time of writing, and compiles in seconds.
* **Portable:** Ground's code only uses features from the C++ standard library, using features from C++17 and prior. * **Portable:** Ground's code only uses features from the C++ standard library, using features from C++17 and prior.
## How do I get started? ## How do I get started?

107
docs/demos/calc.grnd Executable file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/env ground
@begin
stdout "Calculation: "
stdin &calc
getstrsize $calc &calcsize
set &counter 0
set &left ""
set &right ""
set &operator ' '
set &isRight false
## Preprocessing
# Loop to parse input
@loopstart
getstrcharat $calc $counter &char
# Remove any spaces (they're inconvenient)
equal $char ' ' &cond
if $cond %doneif
# Check for operators
# '+' operator
inequal '+' $char &cond
if $cond %minusOpCheck
set &operator $char
set &isRight true
jump %doneif
@minusOpCheck
# '-' operator
inequal '-' $char &cond
if $cond %multiplyOpCheck
set &operator $char
set &isRight true
jump %doneif
@multiplyOpCheck
# '*' operator
inequal '*' $char &cond
if $cond %divideOpCheck
set &operator $char
set &isRight true
jump %doneif
@divideOpCheck
# '/' operator
inequal '/' $char &cond
if $cond %endOpChecks
set &operator $char
set &isRight true
jump %doneif
@endOpChecks
if $isRight %isRight
add $left $char &left
jump %doneif
@isRight
add $right $char &right
@doneif
add 1 $counter &counter
inequal $counter $calcsize &cond
if $cond %loopstart
# End loop
## Computing
# Convert types
stod $left &left
stod $right &right
# Calculations
# Adding
inequal $operator '+' &cond
if $cond %subtract
add $left $right &result
stdlnout $result
jump %begin
# Subtracting
@subtract
inequal $operator '-' &cond
if $cond %multiply
subtract $left $right &result
stdlnout $result
jump %begin
# Multiplying
@multiply
inequal $operator '*' &cond
if $cond %divide
multiply $left $right &result
stdlnout $result
jump %begin
# Dividing
@divide
inequal $operator '/' &cond
if $cond %error
divide $left $right &result
stdlnout $result
jump %begin
@error
stdlnout "Uh oh something terribly terrible happened lmao"
jump %begin

21
docs/library-guide.md Normal file
View File

@@ -0,0 +1,21 @@
# Guide to Writing Libraries in Ground
Ground has a "use" keyword which allows you to import libraries written in Ground, executing the code, and importing functions for use. This makes building reproducable bits of code very easy.
This is a guide of best practices which should be followed.
## .grnd file extension
The Ground interpreter will automatically append ".grnd" when you use a library. If you write `use "myLibrary"` Ground will look for "myLibrary.grnd". This is a must-do.
## camelCase Function and File Names
For consistency, please use camelCase (with a lower case first letter) when naming functions and file names.
## Don't use spaces
It is impossible to use spaces in Ground function names. Please do not use spaces in file names, even though it will work.
## Use functions for most operations
Where possible, create functions to do everything needed with your library. You can include some code for initialisation, but don't do the entirety of your operations outside of your functions.

View File

@@ -50,6 +50,12 @@ Reference a list (a list reference) with an asterisk:
setlist *myList $value1 $value2 # and so on setlist *myList $value1 $value2 # and so on
``` ```
Add comments with a `#`:
```
# This is a comment
```
## Keywords ## Keywords
Note: &var can be replaced with any direct reference. $value can be replaced with a literal value or a value reference. %1 can be replaced with a line reference. Note: &var can be replaced with any direct reference. $value can be replaced with a literal value or a value reference. %1 can be replaced with a line reference.
@@ -188,6 +194,12 @@ Checks if two values are not equal. Outputs a boolean to a direct reference.
Usage: `inequal $value $value &var` Usage: `inequal $value $value &var`
#### not
Negates a boolean.
Usage: `not $value &var`
#### greater #### greater
Checks if the left value is greater than the right value. Outputs a boolean to a direct reference. Checks if the left value is greater than the right value. Outputs a boolean to a direct reference.
@@ -220,7 +232,7 @@ Converts any type to a string.
Usage: `tostring $value &var` Usage: `tostring $value &var`
### Functions and function specific features (ALL WORK IN PROGRESS) ### Functions and function specific features (Experimental, please report bugs!)
Some symbols specific to this category: Some symbols specific to this category:
@@ -260,20 +272,22 @@ Calls a function, with all the arguments in the argument list. The return value
Usage: `call !function &var Usage: `call !function &var
### Interacting with Libraries (ALL WORK IN PROGRESS) ### Interacting with Libraries
#### use #### use (Experimental, please report bugs!)
Attempts to import another Ground program. Gets inserted wherever the use statement is. Any code (including code outside function declarations) will be executed. Attempts to import another Ground program. Gets inserted wherever the use statement is. Any code (including code outside function declarations) will be executed.
Note: Ground will check the directory where the program is stored when trying to find imported programs. If that fails, it will check the directory set in the $GROUND_PATH environment variable set by your system. The '.grnd' extension is appended automatically. Note: Ground will check the directory where the program is being run from when trying to find imported programs. If that fails, it will check the directory set in the $GROUND_LIBS environment variable set by your system. The '.grnd' extension is appended automatically.
Usage: `use $stringvalue` Usage: `use $stringvalue`
#### extern #### extern (Experimental, please report bugs!)
Attempts to import a shared object library written for Ground. All functions in the external library will be usable with `call`. Attempts to import a shared object library written for Ground. All functions in the external library will be usable with `call`.
Note: Ground will check the directory where the program is stored when trying to find external programs. If that fails, it will check the directory set in the $GROUND_PATH environment variable set by your system. The '.so', '.dll', etc extension is appended automatically. Note: Ground will check the directory set in the $GROUND_LIBS environment variable set by your system. The '.so' (Linux), '.dylib' (macOS), or '.dll' (Windows) extension is appended automatically.
Usage: `extern $stringvalue` Documentation on how to do external libraries coming soon.
Usage: `extern $stringvalue`

25
docs/writing-a-program.md Normal file
View File

@@ -0,0 +1,25 @@
## Writing programs with Ground (WORK IN PROGRESS GUIDE)
Ground is a very easy language to learn. In this guide, you will learn how to write a simple calculator in Ground, as well as best practices (which there aren't many of, since Ground is so simple).
Note: This assumes you've read and understand the syntax.md document in this folder.
### Let's start!
Open a new file with the `.grnd` extension. Should look something like this:
```
```
(Real empty in that file.)
Let's add some code! Create a label for the start, since we'll loop back once we've calculated, and ask for the user's input:
```
@start
stdout "Calculation: "
stdin &calc
```
At the calc variable, we have whatever the user typed in. This should look something like `9+10`, `1 / 3` or who knows what else. But first we should organise this data. Let's create a loop to iterate through this input, and a counter to keep moving forward:

43
extlibs/file.cpp Normal file
View File

@@ -0,0 +1,43 @@
#include "ground_lib.h"
#include <fstream>
#include <filesystem>
GroundValue readFile(GroundValue* args, int arg_count) {
VALIDATE_ARGS_1(GROUND_STRING);
std::ifstream ffile(GET_STRING(args[0]));
std::string tmp;
std::string out;
while (std::getline(ffile, tmp)) {
out += tmp + "\n";
}
return ground_string_val(out);
}
GroundValue writeFile(GroundValue* args, int arg_count) {
VALIDATE_ARGS_2(GROUND_STRING, GROUND_STRING);
std::ofstream file(GET_STRING(args[0]));
if (file.good()) {
file << GET_STRING(args[1]);
} else {
std::cout << "File isn't good for writing in" << std::endl;
return GROUND_BOOL_VAL(false);
}
return GROUND_BOOL_VAL(true);
}
GROUND_LIBRARY_INTERFACE()
GROUND_LIBRARY_INIT()
REGISTER_GROUND_FUNCTION(readFile);
REGISTER_GROUND_FUNCTION(writeFile);
GROUND_LIBRARY_INIT_END()
GROUND_LIBRARY_CLEANUP()
GROUND_LIBRARY_CLEANUP_END()

193
extlibs/ground_lib.h Normal file
View File

@@ -0,0 +1,193 @@
#ifndef GROUND_LIB_H
#define GROUND_LIB_H
#include <cstring>
#include <string>
#include <vector>
#include <iostream>
// Ground types - must match the interpreter
typedef enum {
GROUND_INT,
GROUND_DOUBLE,
GROUND_BOOL,
GROUND_STRING,
GROUND_CHAR
} GroundType;
typedef struct {
GroundType type;
union {
int int_val;
double double_val;
int bool_val;
char* string_val;
char char_val;
} data;
} GroundValue;
// Helper macros for creating GroundValue objects
#define GROUND_INT_VAL(x) ({ GroundValue v; v.type = GROUND_INT; v.data.int_val = (x); v; })
#define GROUND_DOUBLE_VAL(x) ({ GroundValue v; v.type = GROUND_DOUBLE; v.data.double_val = (x); v; })
#define GROUND_BOOL_VAL(x) ({ GroundValue v; v.type = GROUND_BOOL; v.data.bool_val = (x) ? 1 : 0; v; })
#define GROUND_CHAR_VAL(x) ({ GroundValue v; v.type = GROUND_CHAR; v.data.char_val = (x); v; })
// Helper function for creating string values
inline GroundValue ground_string_val(const std::string& str) {
GroundValue v;
v.type = GROUND_STRING;
char* result_str = new char[str.length() + 1];
std::strcpy(result_str, str.c_str());
v.data.string_val = result_str;
return v;
}
// Helper function for creating string values from C strings
inline GroundValue ground_cstring_val(const char* str) {
GroundValue v;
v.type = GROUND_STRING;
if (str) {
size_t len = std::strlen(str);
char* result_str = new char[len + 1];
std::strcpy(result_str, str);
v.data.string_val = result_str;
} else {
v.data.string_val = nullptr;
}
return v;
}
// Helper macros for type checking
#define IS_INT(v) ((v).type == GROUND_INT)
#define IS_DOUBLE(v) ((v).type == GROUND_DOUBLE)
#define IS_BOOL(v) ((v).type == GROUND_BOOL)
#define IS_STRING(v) ((v).type == GROUND_STRING)
#define IS_CHAR(v) ((v).type == GROUND_CHAR)
// Helper macros for extracting values
#define GET_INT(v) ((v).data.int_val)
#define GET_DOUBLE(v) ((v).data.double_val)
#define GET_BOOL(v) ((v).data.bool_val != 0)
#define GET_STRING(v) ((v).data.string_val)
#define GET_CHAR(v) ((v).data.char_val)
// Helper macros for argument validation
#define REQUIRE_ARGS(count) \
if (arg_count < (count)) { \
std::cerr << "Error: Expected at least " << (count) << " arguments, got " << arg_count << std::endl; \
return GROUND_BOOL_VAL(false); \
}
#define REQUIRE_TYPE(arg_index, expected_type) \
if (args[arg_index].type != expected_type) { \
std::cerr << "Error: Argument " << (arg_index + 1) << " must be of type " << #expected_type << std::endl; \
return GROUND_BOOL_VAL(false); \
}
// Convenience macro for checking both arg count and types
#define VALIDATE_ARGS_1(type1) \
REQUIRE_ARGS(1); \
REQUIRE_TYPE(0, type1);
#define VALIDATE_ARGS_2(type1, type2) \
REQUIRE_ARGS(2); \
REQUIRE_TYPE(0, type1); \
REQUIRE_TYPE(1, type2);
#define VALIDATE_ARGS_3(type1, type2, type3) \
REQUIRE_ARGS(3); \
REQUIRE_TYPE(0, type1); \
REQUIRE_TYPE(1, type2); \
REQUIRE_TYPE(2, type3);
// Function registration helpers
class GroundLibrary {
private:
std::vector<std::string> function_names;
std::vector<void*> function_pointers;
public:
void register_function(const std::string& name, void* ptr) {
function_names.push_back(name);
function_pointers.push_back(ptr);
}
const char** get_function_names() {
static std::vector<const char*> names;
names.clear();
for (const auto& name : function_names) {
names.push_back(name.c_str());
}
names.push_back(nullptr); // Null terminator
return names.data();
}
void* get_function(const char* name) {
for (size_t i = 0; i < function_names.size(); i++) {
if (function_names[i] == name) {
return function_pointers[i];
}
}
return nullptr;
}
};
// Global library instance
extern GroundLibrary ground_lib_registry;
// Macro to register functions easily
#define REGISTER_GROUND_FUNCTION(func_name) \
ground_lib_registry.register_function(#func_name, (void*)func_name)
// Macro to define the library interface
#define GROUND_LIBRARY_INTERFACE() \
GroundLibrary ground_lib_registry; \
extern "C" { \
const char** ground_get_functions() { \
return ground_lib_registry.get_function_names(); \
} \
void* ground_get_function(const char* name) { \
return ground_lib_registry.get_function(name); \
} \
}
// Optional initialization macro
#define GROUND_LIBRARY_INIT() \
extern "C" { \
void ground_lib_init() {
#define GROUND_LIBRARY_INIT_END() \
} \
}
// Optional cleanup macro
#define GROUND_LIBRARY_CLEANUP() \
extern "C" { \
void ground_lib_cleanup() {
#define GROUND_LIBRARY_CLEANUP_END() \
} \
}
// Utility function to print GroundValue for debugging
inline void debug_print_ground_value(const GroundValue& v) {
switch (v.type) {
case GROUND_INT:
std::cout << "INT: " << v.data.int_val << std::endl;
break;
case GROUND_DOUBLE:
std::cout << "DOUBLE: " << v.data.double_val << std::endl;
break;
case GROUND_BOOL:
std::cout << "BOOL: " << (v.data.bool_val ? "true" : "false") << std::endl;
break;
case GROUND_STRING:
std::cout << "STRING: " << (v.data.string_val ? v.data.string_val : "(null)") << std::endl;
break;
case GROUND_CHAR:
std::cout << "CHAR: '" << v.data.char_val << "'" << std::endl;
break;
}
}
#endif // GROUND_LIB_H

45
extlibs/request.cpp Normal file
View File

@@ -0,0 +1,45 @@
#include "ground_lib.h"
#include <cpr/cpr.h>
#include <cpr/interface.h>
#include <fstream>
GroundValue simpleRequest(GroundValue* args, int arg_count) {
VALIDATE_ARGS_1(GROUND_STRING);
cpr::Response r = cpr::Get(cpr::Url(GET_STRING(args[0])));
if (!(r.status_code >= 200 && r.status_code < 300)) {
return ground_string_val("Error code " + std::to_string(r.status_code));
}
return ground_string_val(r.text);
}
GroundValue saveContents(GroundValue* args, int arg_count) {
VALIDATE_ARGS_2(GROUND_STRING, GROUND_STRING);
std::ofstream file(GET_STRING(args[1]), std::ios::binary);
if (file.good()) {
cpr::Response r = cpr::Download(file, cpr::Url{GET_STRING(args[0])});
if (r.status_code >= 200 && r.status_code < 300) {
return GROUND_BOOL_VAL(false);
}
} else {
return GROUND_BOOL_VAL(false);
}
return GROUND_BOOL_VAL(true);
}
GROUND_LIBRARY_INTERFACE()
GROUND_LIBRARY_INIT()
REGISTER_GROUND_FUNCTION(simpleRequest);
REGISTER_GROUND_FUNCTION(saveContents);
GROUND_LIBRARY_INIT_END()
GROUND_LIBRARY_CLEANUP()
GROUND_LIBRARY_CLEANUP_END()

View File

@@ -39,8 +39,26 @@
#include <variant> #include <variant>
#include <vector> #include <vector>
#include <map> #include <map>
#include <stack>
#include <fstream> #include <fstream>
#include <cstdlib>
#include <filesystem>
// Headers for external libraries
#ifdef _WIN32
// Note: Windows support is experiemental. Maybe try using a superior
// operating system? (cough cough, Linux?)
#include <windows.h>
#define DLOPEN(path) LoadLibrary(path)
#define DLSYM(handle, name) GetProcAddress(handle, name)
#define DLCLOSE(handle) FreeLibrary(handle)
#define DLERROR() "Windows DLL Error"
#else
#include <dlfcn.h>
#define DLOPEN(path) dlopen(path, RTLD_LAZY)
#define DLSYM(handle, name) dlsym(handle, name)
#define DLCLOSE(handle) dlclose(handle)
#define DLERROR() dlerror()
#endif
using namespace std; using namespace std;
@@ -53,7 +71,7 @@ enum class Instructions {
Jump, If, Jump, If,
Stdout, Stdin, Stdlnout, Stdout, Stdin, Stdlnout,
Add, Subtract, Multiply, Divide, Add, Subtract, Multiply, Divide,
Equal, Inequal, Greater, Lesser, Equal, Inequal, Greater, Lesser, Not,
End, Set, Empty, End, Set, Empty,
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend, Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
Getstrcharat, Getstrsize, Getstrcharat, Getstrsize,
@@ -239,16 +257,42 @@ struct Instruction {
vector<variant<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line>> args; vector<variant<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line>> args;
}; };
struct FnArg {
Direct ref;
Types type;
};
/* /*
Function struct Function struct
Contains information needed to run a Ground function. Contains information needed to run a Ground function.
*/ */
struct Function { struct Function {
Types returnType; Types returnType;
vector<pair<Direct, Types>> args; vector<FnArg> args;
vector<Instruction> instructions; vector<Instruction> instructions;
map<string, int> localLabels;
}; };
// C-compatible enum and types for developing libraries for Ground in C
typedef enum {
GROUND_INT,
GROUND_DOUBLE,
GROUND_BOOL,
GROUND_STRING,
GROUND_CHAR
} GroundType;
typedef struct {
GroundType type;
union {
int int_val;
double double_val;
int bool_val;
char* string_val;
char char_val;
} data;
} GroundValue;
/* /*
functions map functions map
Contains the code of functions and types of their values Contains the code of functions and types of their values
@@ -261,11 +305,57 @@ map<string, Function> functions;
*/ */
vector<Literal> fnArgs; vector<Literal> fnArgs;
/* // External library functions and other things
localArgStack stack
Contains the variables in a scope // Handle to loaded libraries
*/ map<string, void*> loadedLibraries;
stack<vector<Literal>> localArgStack;
// Map of function name to function pointer
map<string, void*> externalFunctions;
// Conversion functions
GroundValue literalToGroundValue(const Literal& lit) {
GroundValue gv;
if (holds_alternative<int>(lit.val)) {
gv.type = GROUND_INT;
gv.data.int_val = get<int>(lit.val);
} else if (holds_alternative<double>(lit.val)) {
gv.type = GROUND_DOUBLE;
gv.data.double_val = get<double>(lit.val);
} else if (holds_alternative<bool>(lit.val)) {
gv.type = GROUND_BOOL;
gv.data.bool_val = get<bool>(lit.val) ? 1 : 0;
} else if (holds_alternative<string>(lit.val)) {
gv.type = GROUND_STRING;
gv.data.string_val = const_cast<char*>(get<string>(lit.val).c_str());
} else if (holds_alternative<char>(lit.val)) {
gv.type = GROUND_CHAR;
gv.data.char_val = get<char>(lit.val);
}
return gv;
}
Literal groundValueToLiteral(const GroundValue& gv) {
Literal lit;
switch (gv.type) {
case GROUND_INT:
lit.val = gv.data.int_val;
break;
case GROUND_DOUBLE:
lit.val = gv.data.double_val;
break;
case GROUND_BOOL:
lit.val = (gv.data.bool_val != 0);
break;
case GROUND_STRING:
lit.val = string(gv.data.string_val);
break;
case GROUND_CHAR:
lit.val = gv.data.char_val;
break;
}
return lit;
}
/* /*
error function error function
@@ -377,18 +467,49 @@ Types getType(string in) {
} }
/* /*
exec function getLitType function
This function takes a list of instructions (see Instruction struct above and parser This function determines the type of a value inside a Literal based on the
function below) and acts upon their properties. This is the main interpreter holds_alternative() function. Returns a type from the Types enum class.
function for the program.
*/ */
Types getLitType(Literal in) {
if (holds_alternative<int>(in.val)) return Types::Int;
if (holds_alternative<double>(in.val)) return Types::Double;
if (holds_alternative<bool>(in.val)) return Types::Bool;
if (holds_alternative<string>(in.val)) return Types::String;
if (holds_alternative<char>(in.val)) return Types::Char;
error("Literal for some reason has a weird type. This is not an issue with your program, but an issue with the Ground interpreter.");
return Types::Int;
}
bool processingFunction = false; bool processingFunction = false;
string procFnName = ""; string procFnName = "";
bool inFunction = false; bool inFunction = false;
Literal exec(vector<Instruction> in) { // Forward declaration for the call instruction and use instruction
Literal exec(vector<Instruction> in);
// Forward declaration for the use instruction
vector<Instruction> parser(vector<vector<string>> in);
// Forward declaration for the use instruction
vector<vector<string>> lexer(string in);
/*
exec function
This function takes a list of instructions (see Instruction struct above and parser
function below) and acts upon their properties. This is the main interpreter
function for the program.
*/
Literal exec(vector<Instruction> in, bool executingFunction) {
map<string, int>* currentLabels = &labels;
for (auto& [fnName, fn] : functions) {
if (&fn.instructions == &in) {
currentLabels = &fn.localLabels;
break;
}
}
for (int i = 0; i < in.size(); i++) { for (int i = 0; i < in.size(); i++) {
Instruction l = in[i]; Instruction l = in[i];
if (processingFunction) { if (processingFunction) {
@@ -412,9 +533,9 @@ Literal exec(vector<Instruction> in) {
} else if (holds_alternative<Line>(l.args[j])) { } else if (holds_alternative<Line>(l.args[j])) {
Line ln = get<Line>(l.args[j]); Line ln = get<Line>(l.args[j]);
if (ln.isLabel) { if (ln.isLabel) {
if (labels.find(ln.label) != labels.end()) { if (currentLabels->find(ln.label) != currentLabels->end()) {
Line newLine; Line newLine;
newLine.lineNum = labels[ln.label]; newLine.lineNum = (*currentLabels)[ln.label];
l.args[j] = newLine; l.args[j] = newLine;
} else { } else {
error("Could not find label " + ln.label); error("Could not find label " + ln.label);
@@ -507,11 +628,11 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Set inbuilt"); error("Could not find all arguments required for Set inbuilt");
} }
{ {
string varName; Direct varRef;
Literal varContents; Literal varContents;
if (holds_alternative<Direct>(l.args[0])) { if (holds_alternative<Direct>(l.args[0])) {
varName = get<Direct>(l.args[0]).varName; varRef = get<Direct>(l.args[0]);
} else { } else {
error("First argument of set must be a direct reference"); error("First argument of set must be a direct reference");
} }
@@ -521,7 +642,9 @@ Literal exec(vector<Instruction> in) {
} else { } else {
error("Second argument of set must be a value (literal or value reference)"); error("Second argument of set must be a value (literal or value reference)");
} }
variables[varName] = varContents;
bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = varContents;
} }
break; break;
/* /*
@@ -593,6 +716,7 @@ Literal exec(vector<Instruction> in) {
if (lists.find(listref.listName) != lists.end()) { if (lists.find(listref.listName) != lists.end()) {
if (lists[listref.listName].val.size() > ref) { if (lists[listref.listName].val.size() > ref) {
bool existed = variables.count(var.varName) > 0;
variables[var.varName] = lists[listref.listName].val[ref]; variables[var.varName] = lists[listref.listName].val[ref];
} else { } else {
error("Index " + to_string(ref) + " out of range of list " + listref.listName); error("Index " + to_string(ref) + " out of range of list " + listref.listName);
@@ -620,31 +744,32 @@ Literal exec(vector<Instruction> in) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) { if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
instr = get<string>(get<Literal>(l.args[0]).val); instr = get<string>(get<Literal>(l.args[0]).val);
} else { } else {
error("First argument of getlistat must be a string literal"); error("First argument of getstrcharat must be a string literal");
} }
} else { } else {
error("First argument of getlistat must be a string literal"); error("First argument of getstrcharat must be a string literal");
} }
if (holds_alternative<Literal>(l.args[1])) { if (holds_alternative<Literal>(l.args[1])) {
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) { if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
ref = get<int>(get<Literal>(l.args[1]).val); ref = get<int>(get<Literal>(l.args[1]).val);
} else { } else {
error("Second argument of getlistat must be an integer literal"); error("Second argument of getstrcharat must be an integer literal");
} }
} else { } else {
error("Second argument of getlistat must be an integer literal"); error("Second argument of getstrcharat must be an integer literal");
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
var = get<Direct>(l.args[2]); var = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of getlistat must be a direct reference"); error("Third argument of getstrcharat must be a direct reference");
} }
if (instr.size() > ref) { if (instr.size() > ref) {
Literal newLit; Literal newLit;
newLit.val = instr[ref]; newLit.val = instr[ref];
bool existed = variables.count(var.varName) > 0;
variables[var.varName] = newLit; variables[var.varName] = newLit;
} else { } else {
error("Index " + to_string(ref) + " out of range of string " + instr); error("Index " + to_string(ref) + " out of range of string " + instr);
@@ -755,6 +880,7 @@ Literal exec(vector<Instruction> in) {
Literal newLit; Literal newLit;
if (lists.find(ref.listName) != lists.end()) { if (lists.find(ref.listName) != lists.end()) {
newLit.val = int(lists[ref.listName].val.size()); newLit.val = int(lists[ref.listName].val.size());
bool existed = variables.count(var.varName) > 0;
variables[var.varName] = newLit; variables[var.varName] = newLit;
} else { } else {
error("Couldn't find the list " + ref.listName); error("Couldn't find the list " + ref.listName);
@@ -792,6 +918,7 @@ Literal exec(vector<Instruction> in) {
Literal newLit; Literal newLit;
newLit.val = int(ref.size()); newLit.val = int(ref.size());
bool existed = variables.count(var.varName) > 0;
variables[var.varName] = newLit; variables[var.varName] = newLit;
break; break;
@@ -828,6 +955,7 @@ Literal exec(vector<Instruction> in) {
if (isInt(toConv)) { if (isInt(toConv)) {
Literal newLit; Literal newLit;
newLit.val = stoi(toConv); newLit.val = stoi(toConv);
bool existed = variables.count(ref.varName) > 0;
variables[ref.varName] = newLit; variables[ref.varName] = newLit;
} else { } else {
error("Cannot convert the value " + toConv + " to an int"); error("Cannot convert the value " + toConv + " to an int");
@@ -866,6 +994,7 @@ Literal exec(vector<Instruction> in) {
if (isDouble(toConv) || isInt(toConv)) { if (isDouble(toConv) || isInt(toConv)) {
Literal newLit; Literal newLit;
newLit.val = stod(toConv); newLit.val = stod(toConv);
bool existed = variables.count(ref.varName) > 0;
variables[ref.varName] = newLit; variables[ref.varName] = newLit;
} else { } else {
error("Cannot convert the value " + toConv + " to a decimal"); error("Cannot convert the value " + toConv + " to a decimal");
@@ -913,6 +1042,7 @@ Literal exec(vector<Instruction> in) {
} }
} }
bool existed = variables.count(ref.varName) > 0;
variables[ref.varName] = newLit; variables[ref.varName] = newLit;
} }
@@ -927,11 +1057,13 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Stdin inbuilt"); error("Could not find all arguments required for Stdin inbuilt");
} }
if (holds_alternative<Direct>(l.args[0])) { if (holds_alternative<Direct>(l.args[0])) {
Direct varRef = get<Direct>(l.args[0]);
string userIn; string userIn;
getline(cin, userIn); getline(cin, userIn);
Literal userLit; Literal userLit;
userLit.val = userIn; userLit.val = userIn;
variables[get<Direct>(l.args[0]).varName] = userLit; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = userLit;
} else { } else {
error("First argument of stdin must be a direct reference"); error("First argument of stdin must be a direct reference");
} }
@@ -946,7 +1078,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Add inbuilt"); error("Could not find all arguments required for Add inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -964,7 +1096,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of add must be a direct reference"); error("Third argument of add must be a direct reference");
} }
@@ -990,7 +1122,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot add those two values"); error("Cannot add those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1003,7 +1136,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Subtract inbuilt"); error("Could not find all arguments required for Subtract inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1021,7 +1154,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of subtract must be a direct reference"); error("Third argument of subtract must be a direct reference");
} }
@@ -1039,7 +1172,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot subtract those two values"); error("Cannot subtract those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1052,7 +1186,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Multiply inbuilt"); error("Could not find all arguments required for Multiply inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1070,7 +1204,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of multiply must be a direct reference"); error("Third argument of multiply must be a direct reference");
} }
@@ -1088,7 +1222,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot multiply those two values"); error("Cannot multiply those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1101,7 +1236,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Divide inbuilt"); error("Could not find all arguments required for Divide inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1119,7 +1254,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of divide must be a direct reference"); error("Third argument of divide must be a direct reference");
} }
@@ -1149,7 +1284,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot divide those two values"); error("Cannot divide those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1162,7 +1298,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Equal inbuilt"); error("Could not find all arguments required for Equal inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1180,7 +1316,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of equal must be a direct reference"); error("Third argument of equal must be a direct reference");
} }
@@ -1204,7 +1340,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot equal those two values"); error("Cannot equal those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1217,7 +1354,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Inequal inbuilt"); error("Could not find all arguments required for Inequal inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1235,7 +1372,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of inequal must be a direct reference"); error("Third argument of inequal must be a direct reference");
} }
@@ -1259,7 +1396,41 @@ Literal exec(vector<Instruction> in) {
error("Cannot inequal those two values"); error("Cannot inequal those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
}
break;
/*
not instruction
Negates a boolean.
*/
case Instructions::Not:
if (l.args.size() < 2) {
error("Could not find all arguments required for Not inbuilt");
}
{
Literal boolean;
Direct varRef;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<bool>(get<Literal>(l.args[0]).val)) {
boolean.val = !(get<bool>(get<Literal>(l.args[0]).val));
} else {
error("First argument of not must be a boolean literal");
}
} else {
error("First argument of not must be a boolean literal");
}
if (holds_alternative<Direct>(l.args[1])) {
varRef = get<Direct>(l.args[1]);
} else {
error("Second argument of not must be a direct reference");
}
bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = boolean;
} }
break; break;
/* /*
@@ -1272,7 +1443,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Greater inbuilt"); error("Could not find all arguments required for Greater inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1290,7 +1461,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of greater must be a direct reference"); error("Third argument of greater must be a direct reference");
} }
@@ -1314,7 +1485,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot greater those two values"); error("Cannot greater those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1327,7 +1499,7 @@ Literal exec(vector<Instruction> in) {
error("Could not find all arguments required for Lesser inbuilt"); error("Could not find all arguments required for Lesser inbuilt");
} }
{ {
string varName; Direct varRef;
Literal left; Literal left;
Literal right; Literal right;
Literal final; Literal final;
@@ -1345,7 +1517,7 @@ Literal exec(vector<Instruction> in) {
} }
if (holds_alternative<Direct>(l.args[2])) { if (holds_alternative<Direct>(l.args[2])) {
varName = get<Direct>(l.args[2]).varName; varRef = get<Direct>(l.args[2]);
} else { } else {
error("Third argument of lesser must be a direct reference"); error("Third argument of lesser must be a direct reference");
} }
@@ -1369,7 +1541,8 @@ Literal exec(vector<Instruction> in) {
error("Cannot lesser those two values"); error("Cannot lesser those two values");
} }
variables[varName] = final; bool existed = variables.count(varRef.varName) > 0;
variables[varRef.varName] = final;
} }
break; break;
/* /*
@@ -1437,12 +1610,12 @@ Literal exec(vector<Instruction> in) {
Allows functions to be defined. Allows functions to be defined.
*/ */
case Instructions::Fun: case Instructions::Fun:
if (l.args.size() < 3) { if (l.args.size() < 2) {
error("Could not find all arguments required for Fun inbuilt"); error("Could not find all arguments required for Fun inbuilt");
} }
{ {
Function newFunction; Function newFunction;
if (holds_alternative<TypeRef>(l.args[0])) { if (holds_alternative<TypeRef>(l.args[0])) {
newFunction.returnType = get<TypeRef>(l.args[0]).type; newFunction.returnType = get<TypeRef>(l.args[0]).type;
} else { } else {
@@ -1450,38 +1623,38 @@ Literal exec(vector<Instruction> in) {
} }
string fnName; string fnName;
if (holds_alternative<FunctionRef>(l.args[1])) { if (holds_alternative<FunctionRef>(l.args[1])) {
fnName = get<FunctionRef>(l.args[1]).fnName; fnName = get<FunctionRef>(l.args[1]).fnName;
} else { } else {
error("Second argument of function must be a function reference"); error("Second argument of function must be a function reference");
} }
Types argType; // Parse function arguments (type-direct pairs)
Direct ref; if ((l.args.size() - 2) % 2 != 0) {
bool expectingType = true; error("Function arguments must be in type-direct pairs");
for (int m = 2; m < l.args.size(); m++) {
if (expectingType) {
if (holds_alternative<TypeRef>(l.args[m])) {
argType = get<TypeRef>(l.args[m]).type;
} else {
error("Functions expect a type reference, then a direct reference. Missing a type reference.");
}
} else {
if (holds_alternative<Direct>(l.args[m])) {
ref = get<Direct>(l.args[m]);
} else {
error("Functions expect a type reference, then a direct reference. Missing a direct reference.");
}
newFunction.args.push_back(pair(ref, argType));
}
expectingType = !expectingType;
}
if (!expectingType) {
error("Incomplete function definition, expecting a direct reference after type reference");
} }
for (int m = 2; m < l.args.size(); m += 2) {
FnArg newArg;
// Get type
if (holds_alternative<TypeRef>(l.args[m])) {
newArg.type = get<TypeRef>(l.args[m]).type;
} else {
error("Expected type reference in function argument definition");
}
// Get direct reference
if (m + 1 < l.args.size() && holds_alternative<Direct>(l.args[m + 1])) {
newArg.ref = get<Direct>(l.args[m + 1]);
} else {
error("Expected direct reference after type reference in function argument definition");
}
newFunction.args.push_back(newArg);
}
functions[fnName] = newFunction; functions[fnName] = newFunction;
processingFunction = true; processingFunction = true;
procFnName = fnName; procFnName = fnName;
@@ -1508,6 +1681,10 @@ Literal exec(vector<Instruction> in) {
case Instructions::Endfun: case Instructions::Endfun:
error("No function is being defined. Cannot end function declaration here"); error("No function is being defined. Cannot end function declaration here");
break; break;
/*
pusharg instruction
This instruction makes new arguments avaliable for functions.
*/
case Instructions::Pusharg: case Instructions::Pusharg:
if (l.args.size() < 1) { if (l.args.size() < 1) {
error("Could not find all arguments required for Endfun inbuilt"); error("Could not find all arguments required for Endfun inbuilt");
@@ -1518,13 +1695,233 @@ Literal exec(vector<Instruction> in) {
error("First argument of pusharg must be a literal"); error("First argument of pusharg must be a literal");
} }
break; break;
case Instructions::Local: /*
break; call instruction
This instruction calls a function, and makes the arguments avaliable for it.
*/
case Instructions::Call: case Instructions::Call:
{
if (l.args.size() < 2) {
error("Could not find all arguments required for Call inbuilt");
}
FunctionRef ref;
Direct returnRef;
if (holds_alternative<FunctionRef>(l.args[0])) {
ref = get<FunctionRef>(l.args[0]);
} else {
error("First argument of call must be a function reference");
}
if (holds_alternative<Direct>(l.args[1])) {
returnRef = get<Direct>(l.args[1]);
} else {
error("Second argument of call must be a direct reference");
}
// Check for external function
if (externalFunctions.find(ref.fnName) != externalFunctions.end()) {
// Call external function
typedef GroundValue (*ExtFunc)(GroundValue*, int);
ExtFunc extFunc = (ExtFunc)externalFunctions[ref.fnName];
// Convert arguments
vector<GroundValue> gvArgs;
for (const Literal& arg : fnArgs) {
gvArgs.push_back(literalToGroundValue(arg));
}
// Call function
GroundValue result = extFunc(gvArgs.data(), gvArgs.size());
// Convert result back
Literal resultLit = groundValueToLiteral(result);
// Clear arguments and store result
fnArgs.clear();
variables[returnRef.varName] = resultLit;
break;
}
// Check if function exists
if (functions.find(ref.fnName) == functions.end()) {
error("Function " + ref.fnName + " not found");
}
// Check argument count
if (fnArgs.size() != functions[ref.fnName].args.size()) {
error("Function " + ref.fnName + " expects " +
to_string(functions[ref.fnName].args.size()) +
" arguments, got " + to_string(fnArgs.size()));
}
// Create backup of variables to be restored
map<string, Literal> scopeBackup = variables;
// Create function arguments with type checking
for (int m = 0; m < functions[ref.fnName].args.size(); m++) {
FnArg arg = functions[ref.fnName].args[m];
// Type checking - now with error reporting
if (arg.type != getLitType(fnArgs[m])) {
error("Function " + ref.fnName + " argument " + to_string(m + 1) +
" type mismatch. Expected type does not match provided argument type.");
}
// Create the variable
variables[arg.ref.varName] = fnArgs[m];
}
// Call the function
Literal retVal = exec(functions[ref.fnName].instructions, true);
// Restore scope
variables = scopeBackup;
// Clear function arguments for next call
fnArgs.clear();
// Now, assign the return value in the current scope.
bool existed = variables.count(returnRef.varName) > 0;
variables[returnRef.varName] = retVal;
}
break; break;
case Instructions::Use: case Instructions::Use:
if (l.args.size() < 1) {
error("Could not find all arguments for Use inbuilt");
}
{
string useName;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
useName = get<string>(get<Literal>(l.args[0]).val) + ".grnd";
} else {
error("First argument for use requires a string literal");
}
} else {
error("First argument for use requires a string literal");
}
string groundLibsDir = getenv("GROUND_LIBS");
if (filesystem::exists(useName)) {
} else if (groundLibsDir != "" && filesystem::exists(groundLibsDir + useName)) {
useName = groundLibsDir + useName;
} else {
error("Could not find external Ground library in $GROUND_LIBS or current directory.");
}
ifstream file(useName);
string lns;
string in;
while (getline(file, lns)) {
in += lns += "\n";
}
Literal ret = exec(parser(lexer(in)), false); }
break; break;
case Instructions::Extern: case Instructions::Extern:
if (l.args.size() < 1) {
error("Could not find all arguments for Extern inbuilt");
}
{
string libName;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
libName = get<string>(get<Literal>(l.args[0]).val);
} else {
error("First argument for extern requires a string literal");
}
} else {
error("First argument for extern requires a string literal");
}
// Add appropriate extension
string fullLibName = libName;
#ifdef _WIN32
fullLibName += ".dll";
#elif __APPLE__
fullLibName += ".dylib";
#else
fullLibName += ".so";
#endif
// Check multiple locations for the library
string libPath;
bool found = false;
// Check GROUND_LIBS directory
const char* groundLibsDir = getenv("GROUND_LIBS");
if (groundLibsDir) {
string envPath = string(groundLibsDir);
// Add trailing slash if not present
if (!envPath.empty() && envPath.back() != '/' && envPath.back() != '\\') {
envPath += "/";
}
string fullEnvPath = envPath + fullLibName;
if (filesystem::exists(fullEnvPath)) {
libPath = fullEnvPath;
found = true;
}
}
if (!found) {
error("Could not find external library: " + fullLibName +
" (searched current directory and GROUND_LIBS)");
}
// Try to open the library
void* handle = DLOPEN(libPath.c_str());
if (!handle) {
error("Failed to load library " + libPath + ": " + string(DLERROR()));
}
// Store handle for cleanup later
loadedLibraries[libName] = handle;
// Get required functions
typedef const char** (*GetFunctionsFunc)();
typedef void* (*GetFunctionFunc)(const char*);
GetFunctionsFunc getFunctions = (GetFunctionsFunc)DLSYM(handle, "ground_get_functions");
GetFunctionFunc getFunction = (GetFunctionFunc)DLSYM(handle, "ground_get_function");
if (!getFunctions || !getFunction) {
error("Library " + libName + " is not Ground-compatible (missing required functions: ground_get_functions or ground_get_function)");
}
// Optional initialization
typedef void (*InitFunc)();
InitFunc initFunc = (InitFunc)DLSYM(handle, "ground_lib_init");
if (initFunc) {
initFunc();
}
// Register all functions
const char** functionNames = getFunctions();
if (!functionNames) {
error("Library " + libName + " returned null function list");
}
int functionCount = 0;
for (int i = 0; functionNames[i] != nullptr; i++) {
void* funcPtr = getFunction(functionNames[i]);
if (funcPtr) {
externalFunctions[string(functionNames[i])] = funcPtr;
functionCount++;
} else {
error("Failed to get function pointer for: " + string(functionNames[i]));
}
}
if (functionCount == 0) {
error("No functions were loaded from library: " + libName);
}
}
break; break;
default: default:
cout << "Still to be implemented" << endl; cout << "Still to be implemented" << endl;
@@ -1553,14 +1950,22 @@ vector<vector<string>> lexer(string in) {
switch (i) { switch (i) {
case '"': case '"':
if (!isComment) { if (!isComment) {
procString = !procString; if (procChar) {
buf.push_back(i); buf.push_back(i);
} else {
procString = !procString;
buf.push_back(i);
}
} }
break; break;
case '\'': case '\'':
if (!isComment) { if (!isComment) {
procChar = !procChar; if (procString) {
buf.push_back(i); buf.push_back(i);
} else {
procChar = !procChar;
buf.push_back(i);
}
} }
break; break;
case '\n': case '\n':
@@ -1610,12 +2015,14 @@ vector<vector<string>> lexer(string in) {
*/ */
vector<Instruction> parser(vector<vector<string>> in) { vector<Instruction> parser(vector<vector<string>> in) {
vector<Instruction> out; vector<Instruction> out;
int lineNum = 1; int lineNum = 1;
int functionInstructionIndex = 0;
for (vector<string> lineTokens : in) { for (vector<string> lineTokens : in) {
lineNum ++; lineNum ++;
Instruction newInst; Instruction newInst;
if (lineTokens.empty()) { if (lineTokens.empty()) {
if (processingFunction) functionInstructionIndex ++;
out.push_back(newInst); out.push_back(newInst);
continue; continue;
}; };
@@ -1625,9 +2032,11 @@ vector<Instruction> parser(vector<vector<string>> in) {
if (firstInst) { if (firstInst) {
firstInst = false; firstInst = false;
if (isLabel(i)) { if (isLabel(i)) {
labels[i.substr(1)] = lineNum; if (processingFunction) {
//out.push_back(newInst); functions[procFnName].localLabels[i.substr(1)] = functionInstructionIndex;
continue; } else {
labels[i.substr(1)] = lineNum - 1;
}
} }
else if (i == "stdin") newInst.inst = Instructions::Stdin; else if (i == "stdin") newInst.inst = Instructions::Stdin;
else if (i == "stdout") newInst.inst = Instructions::Stdout; else if (i == "stdout") newInst.inst = Instructions::Stdout;
@@ -1642,6 +2051,7 @@ vector<Instruction> parser(vector<vector<string>> in) {
else if (i == "lesser") newInst.inst = Instructions::Lesser; else if (i == "lesser") newInst.inst = Instructions::Lesser;
else if (i == "equal") newInst.inst = Instructions::Equal; else if (i == "equal") newInst.inst = Instructions::Equal;
else if (i == "inequal") newInst.inst = Instructions::Inequal; else if (i == "inequal") newInst.inst = Instructions::Inequal;
else if (i == "not") newInst.inst = Instructions::Not;
else if (i == "end") newInst.inst = Instructions::End; else if (i == "end") newInst.inst = Instructions::End;
else if (i == "set") newInst.inst = Instructions::Set; else if (i == "set") newInst.inst = Instructions::Set;
else if (i == "setlist") newInst.inst = Instructions::Setlist; else if (i == "setlist") newInst.inst = Instructions::Setlist;
@@ -1654,7 +2064,10 @@ vector<Instruction> parser(vector<vector<string>> in) {
else if (i == "stoi") newInst.inst = Instructions::Stoi; else if (i == "stoi") newInst.inst = Instructions::Stoi;
else if (i == "stod") newInst.inst = Instructions::Stod; else if (i == "stod") newInst.inst = Instructions::Stod;
else if (i == "tostring") newInst.inst = Instructions::Tostring; else if (i == "tostring") newInst.inst = Instructions::Tostring;
else if (i == "fun") newInst.inst = Instructions::Fun; else if (i == "fun") {
newInst.inst = Instructions::Fun;
functionInstructionIndex = 0;
}
else if (i == "return") newInst.inst = Instructions::Return; else if (i == "return") newInst.inst = Instructions::Return;
else if (i == "endfun") newInst.inst = Instructions::Endfun; else if (i == "endfun") newInst.inst = Instructions::Endfun;
else if (i == "pusharg") newInst.inst = Instructions::Pusharg; else if (i == "pusharg") newInst.inst = Instructions::Pusharg;
@@ -1764,6 +2177,7 @@ vector<Instruction> parser(vector<vector<string>> in) {
} }
} }
} }
if (processingFunction) functionInstructionIndex++;
out.push_back(newInst); out.push_back(newInst);
} }
return out; return out;
@@ -1780,13 +2194,23 @@ int main(int argc, char** argv) {
cout << "Usage: " << argv[0] << " (file)" << endl; cout << "Usage: " << argv[0] << " (file)" << endl;
exit(0); exit(0);
} }
// Get args
List argsList;
for (int i = 2; i < argc; i++) {
Literal lit;
lit.val = argv[i];
argsList.val.push_back(lit);
}
lists["args"] = argsList;
ifstream file(argv[1]); ifstream file(argv[1]);
string lns; string lns;
string in; string in;
while (getline(file, lns)) { while (getline(file, lns)) {
in += lns += "\n"; in += lns += "\n";
} }
Literal ret = exec(parser(lexer(in))); Literal ret = exec(parser(lexer(in)), false);
if (holds_alternative<int>(ret.val)) { if (holds_alternative<int>(ret.val)) {
return get<int>(ret.val); return get<int>(ret.val);
} else { } else {

12
tests/args.grnd Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env ground
stdlnout "Program args: "
getlistsize *args &argsSize
set &counter 0
@loopstart
equal $counter $argsSize &bool
if $bool %end
getlistat *args $counter &item
stdlnout $item
add 1 $counter &counter
jump %loopstart
@end

View File

@@ -1,6 +1,19 @@
fun -int !dingle -string &silly fun -bool !jumpy
stdlnout &silly stdlnout "This is the jumpy function"
return 10 set &counter 0
jump %inloop
@jumpback
stdlnout "Yay I jumped!"
@inloop
add 1 $counter &counter
inequal 10 $counter &out
stdout "Condition is"
stdlnout $out
if $out %jumpback
stdlnout "Finished"
return true
endfun endfun
stdlnout "This is outside the function" call !jumpy &tmp
stdlnout "I called a function"

3
tests/use/library.grnd Normal file
View File

@@ -0,0 +1,3 @@
fun -string !dingus
return "Hello from the library"
endfun

5
tests/use/use.grnd Normal file
View File

@@ -0,0 +1,5 @@
use "library"
call !dingus &var
stdlnout $var