Compare commits
20 Commits
e4cc6b2f14
...
0.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| c952be1fe6 | |||
| 1c5ca8d201 | |||
| e56e6de911 | |||
| 38b17e7db5 | |||
| e74e5ea548 | |||
| f5140e3833 | |||
| c01dd470e1 | |||
| 16660c6a8d | |||
| 5bd8519517 | |||
| 2f706e2285 | |||
| db99b9ac9f | |||
| e906734aca | |||
| 8da5a2bf93 | |||
| e9600d8500 | |||
| 1c0dfcc4b7 | |||
| f7f3972248 | |||
| 50d83aa228 | |||
| 14758df1ab | |||
| b19b4123d8 | |||
| 28a9e389fa |
@@ -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.
|
||||
* **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.
|
||||
|
||||
## How do I get started?
|
||||
|
||||
107
docs/demos/calc.grnd
Executable file
107
docs/demos/calc.grnd
Executable 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
21
docs/library-guide.md
Normal 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.
|
||||
@@ -50,6 +50,12 @@ Reference a list (a list reference) with an asterisk:
|
||||
setlist *myList $value1 $value2 # and so on
|
||||
```
|
||||
|
||||
Add comments with a `#`:
|
||||
|
||||
```
|
||||
# This is a comment
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -188,6 +194,12 @@ Checks if two values are not equal. Outputs a boolean to a direct reference.
|
||||
|
||||
Usage: `inequal $value $value &var`
|
||||
|
||||
#### not
|
||||
|
||||
Negates a boolean.
|
||||
|
||||
Usage: `not $value &var`
|
||||
|
||||
#### greater
|
||||
|
||||
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`
|
||||
|
||||
### Functions and function specific features (ALL WORK IN PROGRESS)
|
||||
### Functions and function specific features (Experimental, please report bugs!)
|
||||
|
||||
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
|
||||
|
||||
### 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.
|
||||
|
||||
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`
|
||||
|
||||
#### 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`.
|
||||
|
||||
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
25
docs/writing-a-program.md
Normal 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
43
extlibs/file.cpp
Normal 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
193
extlibs/ground_lib.h
Normal 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
45
extlibs/request.cpp
Normal 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()
|
||||
596
src/main.cpp
596
src/main.cpp
@@ -39,8 +39,26 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
|
||||
// Headers for external libraries
|
||||
#ifdef _WIN32
|
||||
// Note: Windows support is experiemental. Maybe try using a superior
|
||||
// operating system? (cough cough, Linux?)
|
||||
#include <windows.h>
|
||||
#define DLOPEN(path) LoadLibrary(path)
|
||||
#define DLSYM(handle, name) GetProcAddress(handle, name)
|
||||
#define DLCLOSE(handle) FreeLibrary(handle)
|
||||
#define DLERROR() "Windows DLL Error"
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#define DLOPEN(path) dlopen(path, RTLD_LAZY)
|
||||
#define DLSYM(handle, name) dlsym(handle, name)
|
||||
#define DLCLOSE(handle) dlclose(handle)
|
||||
#define DLERROR() dlerror()
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -53,7 +71,7 @@ enum class Instructions {
|
||||
Jump, If,
|
||||
Stdout, Stdin, Stdlnout,
|
||||
Add, Subtract, Multiply, Divide,
|
||||
Equal, Inequal, Greater, Lesser,
|
||||
Equal, Inequal, Greater, Lesser, Not,
|
||||
End, Set, Empty,
|
||||
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
|
||||
Getstrcharat, Getstrsize,
|
||||
@@ -239,16 +257,42 @@ struct Instruction {
|
||||
vector<variant<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line>> args;
|
||||
};
|
||||
|
||||
struct FnArg {
|
||||
Direct ref;
|
||||
Types type;
|
||||
};
|
||||
|
||||
/*
|
||||
Function struct
|
||||
Contains information needed to run a Ground function.
|
||||
*/
|
||||
struct Function {
|
||||
Types returnType;
|
||||
vector<pair<Direct, Types>> args;
|
||||
vector<FnArg> args;
|
||||
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
|
||||
Contains the code of functions and types of their values
|
||||
@@ -261,11 +305,57 @@ map<string, Function> functions;
|
||||
*/
|
||||
vector<Literal> fnArgs;
|
||||
|
||||
/*
|
||||
localArgStack stack
|
||||
Contains the variables in a scope
|
||||
*/
|
||||
stack<vector<Literal>> localArgStack;
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
@@ -377,18 +467,49 @@ Types getType(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.
|
||||
getLitType function
|
||||
This function determines the type of a value inside a Literal based on the
|
||||
holds_alternative() function. Returns a type from the Types enum class.
|
||||
*/
|
||||
Types getLitType(Literal in) {
|
||||
if (holds_alternative<int>(in.val)) return Types::Int;
|
||||
if (holds_alternative<double>(in.val)) return Types::Double;
|
||||
if (holds_alternative<bool>(in.val)) return Types::Bool;
|
||||
if (holds_alternative<string>(in.val)) return Types::String;
|
||||
if (holds_alternative<char>(in.val)) return Types::Char;
|
||||
error("Literal for some reason has a weird type. This is not an issue with your program, but an issue with the Ground interpreter.");
|
||||
return Types::Int;
|
||||
}
|
||||
|
||||
bool processingFunction = false;
|
||||
string procFnName = "";
|
||||
|
||||
bool inFunction = false;
|
||||
|
||||
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++) {
|
||||
Instruction l = in[i];
|
||||
if (processingFunction) {
|
||||
@@ -412,9 +533,9 @@ Literal exec(vector<Instruction> in) {
|
||||
} else if (holds_alternative<Line>(l.args[j])) {
|
||||
Line ln = get<Line>(l.args[j]);
|
||||
if (ln.isLabel) {
|
||||
if (labels.find(ln.label) != labels.end()) {
|
||||
if (currentLabels->find(ln.label) != currentLabels->end()) {
|
||||
Line newLine;
|
||||
newLine.lineNum = labels[ln.label];
|
||||
newLine.lineNum = (*currentLabels)[ln.label];
|
||||
l.args[j] = newLine;
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal varContents;
|
||||
|
||||
if (holds_alternative<Direct>(l.args[0])) {
|
||||
varName = get<Direct>(l.args[0]).varName;
|
||||
varRef = get<Direct>(l.args[0]);
|
||||
} else {
|
||||
error("First argument of set must be a direct reference");
|
||||
}
|
||||
@@ -521,7 +642,9 @@ Literal exec(vector<Instruction> in) {
|
||||
} else {
|
||||
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;
|
||||
/*
|
||||
@@ -593,6 +716,7 @@ Literal exec(vector<Instruction> in) {
|
||||
|
||||
if (lists.find(listref.listName) != lists.end()) {
|
||||
if (lists[listref.listName].val.size() > ref) {
|
||||
bool existed = variables.count(var.varName) > 0;
|
||||
variables[var.varName] = lists[listref.listName].val[ref];
|
||||
} else {
|
||||
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)) {
|
||||
instr = get<string>(get<Literal>(l.args[0]).val);
|
||||
} else {
|
||||
error("First argument of getlistat must be a string literal");
|
||||
error("First argument of getstrcharat must be a string literal");
|
||||
}
|
||||
} 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<int>(get<Literal>(l.args[1]).val)) {
|
||||
ref = get<int>(get<Literal>(l.args[1]).val);
|
||||
} else {
|
||||
error("Second argument of getlistat must be an integer literal");
|
||||
error("Second argument of getstrcharat must be an integer literal");
|
||||
}
|
||||
} 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])) {
|
||||
var = get<Direct>(l.args[2]);
|
||||
} 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) {
|
||||
Literal newLit;
|
||||
newLit.val = instr[ref];
|
||||
bool existed = variables.count(var.varName) > 0;
|
||||
variables[var.varName] = newLit;
|
||||
} else {
|
||||
error("Index " + to_string(ref) + " out of range of string " + instr);
|
||||
@@ -755,6 +880,7 @@ Literal exec(vector<Instruction> in) {
|
||||
Literal newLit;
|
||||
if (lists.find(ref.listName) != lists.end()) {
|
||||
newLit.val = int(lists[ref.listName].val.size());
|
||||
bool existed = variables.count(var.varName) > 0;
|
||||
variables[var.varName] = newLit;
|
||||
} else {
|
||||
error("Couldn't find the list " + ref.listName);
|
||||
@@ -792,6 +918,7 @@ Literal exec(vector<Instruction> in) {
|
||||
|
||||
Literal newLit;
|
||||
newLit.val = int(ref.size());
|
||||
bool existed = variables.count(var.varName) > 0;
|
||||
variables[var.varName] = newLit;
|
||||
|
||||
break;
|
||||
@@ -828,6 +955,7 @@ Literal exec(vector<Instruction> in) {
|
||||
if (isInt(toConv)) {
|
||||
Literal newLit;
|
||||
newLit.val = stoi(toConv);
|
||||
bool existed = variables.count(ref.varName) > 0;
|
||||
variables[ref.varName] = newLit;
|
||||
} else {
|
||||
error("Cannot convert the value " + toConv + " to an int");
|
||||
@@ -866,6 +994,7 @@ Literal exec(vector<Instruction> in) {
|
||||
if (isDouble(toConv) || isInt(toConv)) {
|
||||
Literal newLit;
|
||||
newLit.val = stod(toConv);
|
||||
bool existed = variables.count(ref.varName) > 0;
|
||||
variables[ref.varName] = newLit;
|
||||
} else {
|
||||
error("Cannot convert the value " + toConv + " to a decimal");
|
||||
@@ -913,6 +1042,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
}
|
||||
|
||||
bool existed = variables.count(ref.varName) > 0;
|
||||
variables[ref.varName] = newLit;
|
||||
|
||||
}
|
||||
@@ -927,11 +1057,13 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Stdin inbuilt");
|
||||
}
|
||||
if (holds_alternative<Direct>(l.args[0])) {
|
||||
Direct varRef = get<Direct>(l.args[0]);
|
||||
string userIn;
|
||||
getline(cin, userIn);
|
||||
Literal userLit;
|
||||
userLit.val = userIn;
|
||||
variables[get<Direct>(l.args[0]).varName] = userLit;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = userLit;
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -964,7 +1096,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1003,7 +1136,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Subtract inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1021,7 +1154,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1052,7 +1186,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Multiply inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1070,7 +1204,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1101,7 +1236,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Divide inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1119,7 +1254,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1162,7 +1298,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Equal inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1180,7 +1316,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1217,7 +1354,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Inequal inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1235,7 +1372,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
/*
|
||||
@@ -1272,7 +1443,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Greater inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1290,7 +1461,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1327,7 +1499,7 @@ Literal exec(vector<Instruction> in) {
|
||||
error("Could not find all arguments required for Lesser inbuilt");
|
||||
}
|
||||
{
|
||||
string varName;
|
||||
Direct varRef;
|
||||
Literal left;
|
||||
Literal right;
|
||||
Literal final;
|
||||
@@ -1345,7 +1517,7 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[2])) {
|
||||
varName = get<Direct>(l.args[2]).varName;
|
||||
varRef = get<Direct>(l.args[2]);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
variables[varName] = final;
|
||||
bool existed = variables.count(varRef.varName) > 0;
|
||||
variables[varRef.varName] = final;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1437,12 +1610,12 @@ Literal exec(vector<Instruction> in) {
|
||||
Allows functions to be defined.
|
||||
*/
|
||||
case Instructions::Fun:
|
||||
if (l.args.size() < 3) {
|
||||
if (l.args.size() < 2) {
|
||||
error("Could not find all arguments required for Fun inbuilt");
|
||||
}
|
||||
{
|
||||
Function newFunction;
|
||||
|
||||
|
||||
if (holds_alternative<TypeRef>(l.args[0])) {
|
||||
newFunction.returnType = get<TypeRef>(l.args[0]).type;
|
||||
} else {
|
||||
@@ -1450,38 +1623,38 @@ Literal exec(vector<Instruction> in) {
|
||||
}
|
||||
|
||||
string fnName;
|
||||
|
||||
|
||||
if (holds_alternative<FunctionRef>(l.args[1])) {
|
||||
fnName = get<FunctionRef>(l.args[1]).fnName;
|
||||
} else {
|
||||
error("Second argument of function must be a function reference");
|
||||
}
|
||||
|
||||
Types argType;
|
||||
Direct ref;
|
||||
bool expectingType = true;
|
||||
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");
|
||||
// Parse function arguments (type-direct pairs)
|
||||
if ((l.args.size() - 2) % 2 != 0) {
|
||||
error("Function arguments must be in type-direct pairs");
|
||||
}
|
||||
|
||||
for (int m = 2; m < l.args.size(); m += 2) {
|
||||
FnArg newArg;
|
||||
|
||||
// Get type
|
||||
if (holds_alternative<TypeRef>(l.args[m])) {
|
||||
newArg.type = get<TypeRef>(l.args[m]).type;
|
||||
} else {
|
||||
error("Expected type reference in function argument definition");
|
||||
}
|
||||
|
||||
// Get direct reference
|
||||
if (m + 1 < l.args.size() && holds_alternative<Direct>(l.args[m + 1])) {
|
||||
newArg.ref = get<Direct>(l.args[m + 1]);
|
||||
} else {
|
||||
error("Expected direct reference after type reference in function argument definition");
|
||||
}
|
||||
|
||||
newFunction.args.push_back(newArg);
|
||||
}
|
||||
|
||||
functions[fnName] = newFunction;
|
||||
processingFunction = true;
|
||||
procFnName = fnName;
|
||||
@@ -1508,6 +1681,10 @@ Literal exec(vector<Instruction> in) {
|
||||
case Instructions::Endfun:
|
||||
error("No function is being defined. Cannot end function declaration here");
|
||||
break;
|
||||
/*
|
||||
pusharg instruction
|
||||
This instruction makes new arguments avaliable for functions.
|
||||
*/
|
||||
case Instructions::Pusharg:
|
||||
if (l.args.size() < 1) {
|
||||
error("Could not find all arguments required for Endfun inbuilt");
|
||||
@@ -1518,13 +1695,233 @@ Literal exec(vector<Instruction> in) {
|
||||
error("First argument of pusharg must be a literal");
|
||||
}
|
||||
break;
|
||||
case Instructions::Local:
|
||||
break;
|
||||
/*
|
||||
call instruction
|
||||
This instruction calls a function, and makes the arguments avaliable for it.
|
||||
*/
|
||||
case Instructions::Call:
|
||||
{
|
||||
if (l.args.size() < 2) {
|
||||
error("Could not find all arguments required for Call inbuilt");
|
||||
}
|
||||
|
||||
FunctionRef ref;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
cout << "Still to be implemented" << endl;
|
||||
@@ -1553,14 +1950,22 @@ vector<vector<string>> lexer(string in) {
|
||||
switch (i) {
|
||||
case '"':
|
||||
if (!isComment) {
|
||||
procString = !procString;
|
||||
buf.push_back(i);
|
||||
if (procChar) {
|
||||
buf.push_back(i);
|
||||
} else {
|
||||
procString = !procString;
|
||||
buf.push_back(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
if (!isComment) {
|
||||
procChar = !procChar;
|
||||
buf.push_back(i);
|
||||
if (procString) {
|
||||
buf.push_back(i);
|
||||
} else {
|
||||
procChar = !procChar;
|
||||
buf.push_back(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
@@ -1610,12 +2015,14 @@ vector<vector<string>> lexer(string in) {
|
||||
*/
|
||||
vector<Instruction> parser(vector<vector<string>> in) {
|
||||
vector<Instruction> out;
|
||||
|
||||
int lineNum = 1;
|
||||
int functionInstructionIndex = 0;
|
||||
|
||||
for (vector<string> lineTokens : in) {
|
||||
lineNum ++;
|
||||
Instruction newInst;
|
||||
if (lineTokens.empty()) {
|
||||
if (processingFunction) functionInstructionIndex ++;
|
||||
out.push_back(newInst);
|
||||
continue;
|
||||
};
|
||||
@@ -1625,9 +2032,11 @@ vector<Instruction> parser(vector<vector<string>> in) {
|
||||
if (firstInst) {
|
||||
firstInst = false;
|
||||
if (isLabel(i)) {
|
||||
labels[i.substr(1)] = lineNum;
|
||||
//out.push_back(newInst);
|
||||
continue;
|
||||
if (processingFunction) {
|
||||
functions[procFnName].localLabels[i.substr(1)] = functionInstructionIndex;
|
||||
} else {
|
||||
labels[i.substr(1)] = lineNum - 1;
|
||||
}
|
||||
}
|
||||
else if (i == "stdin") newInst.inst = Instructions::Stdin;
|
||||
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 == "equal") newInst.inst = Instructions::Equal;
|
||||
else if (i == "inequal") newInst.inst = Instructions::Inequal;
|
||||
else if (i == "not") newInst.inst = Instructions::Not;
|
||||
else if (i == "end") newInst.inst = Instructions::End;
|
||||
else if (i == "set") newInst.inst = Instructions::Set;
|
||||
else if (i == "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 == "stod") newInst.inst = Instructions::Stod;
|
||||
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 == "endfun") newInst.inst = Instructions::Endfun;
|
||||
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);
|
||||
}
|
||||
return out;
|
||||
@@ -1780,13 +2194,23 @@ int main(int argc, char** argv) {
|
||||
cout << "Usage: " << argv[0] << " (file)" << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Get args
|
||||
List argsList;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
Literal lit;
|
||||
lit.val = argv[i];
|
||||
argsList.val.push_back(lit);
|
||||
}
|
||||
lists["args"] = argsList;
|
||||
|
||||
ifstream file(argv[1]);
|
||||
string lns;
|
||||
string in;
|
||||
while (getline(file, lns)) {
|
||||
in += lns += "\n";
|
||||
}
|
||||
Literal ret = exec(parser(lexer(in)));
|
||||
Literal ret = exec(parser(lexer(in)), false);
|
||||
if (holds_alternative<int>(ret.val)) {
|
||||
return get<int>(ret.val);
|
||||
} else {
|
||||
|
||||
12
tests/args.grnd
Executable file
12
tests/args.grnd
Executable 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
|
||||
@@ -1,6 +1,19 @@
|
||||
fun -int !dingle -string &silly
|
||||
stdlnout &silly
|
||||
return 10
|
||||
fun -bool !jumpy
|
||||
stdlnout "This is the jumpy function"
|
||||
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
|
||||
|
||||
stdlnout "This is outside the function"
|
||||
call !jumpy &tmp
|
||||
|
||||
stdlnout "I called a function"
|
||||
|
||||
3
tests/use/library.grnd
Normal file
3
tests/use/library.grnd
Normal file
@@ -0,0 +1,3 @@
|
||||
fun -string !dingus
|
||||
return "Hello from the library"
|
||||
endfun
|
||||
5
tests/use/use.grnd
Normal file
5
tests/use/use.grnd
Normal file
@@ -0,0 +1,5 @@
|
||||
use "library"
|
||||
|
||||
call !dingus &var
|
||||
|
||||
stdlnout $var
|
||||
Reference in New Issue
Block a user