forked from ground/ground
Compare commits
23 Commits
0.0.2
...
d9790711c6
Author | SHA1 | Date | |
---|---|---|---|
d9790711c6 | |||
310fede3ec | |||
a4eba4ae47 | |||
e2a037befc | |||
872392c1c5 | |||
2e1e2e727b | |||
38681f72d7 | |||
9c8cd58449 | |||
074b473bb1 | |||
a3b9cd2519 | |||
76205a613d | |||
7961195018 | |||
e73e5a7ebc | |||
06ed44a010 | |||
d8cc3ff9e0 | |||
f32f76450a | |||
cea66aa583 | |||
8d80416c5c | |||
0eb5670dfd | |||
8247ba36e4 | |||
76e36b7ca3 | |||
6596bfcc85 | |||
a9bfc1b0e3 |
70
docs/highlight.py
Normal file
70
docs/highlight.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
exit(1)
|
||||
|
||||
fileName = sys.argv[1]
|
||||
thefile = open(fileName).readlines()
|
||||
|
||||
def isnumber(num):
|
||||
try:
|
||||
float(num)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
allstr = ""
|
||||
color = ""
|
||||
keywords = ["if", "jump", "end", "stdin", "stdout", "stdlnout", "set", "gettype", "exists", "setlist", "setlistat", "getlistat", "getlistsize", "listappend", "getstrsize", "getstrcharat", "add", "subtract", "multiply", "divide", "equal", "inequal", "not", "greater", "lesser", "stoi", "stod", "tostring", "fun", "return", "endfun", "pusharg", "call", "use", "extern"]
|
||||
|
||||
for line in thefile:
|
||||
allstr += line + " <br> "
|
||||
|
||||
lines = len(allstr.split("<br>"))-1
|
||||
a = allstr.split()
|
||||
|
||||
for i in range(lines):
|
||||
instr = False
|
||||
incom = False
|
||||
words = len(allstr.split("<br>")[i].split())
|
||||
for j in range(words):
|
||||
tempword = allstr.split("<br>")[i].split()[j]
|
||||
if allstr.split("<br>")[i].split()[0][0] == "#":
|
||||
color = "\033[37m"
|
||||
elif allstr.split("<br>")[i].split()[0][0] == "@":
|
||||
color = "\033[32m"
|
||||
elif tempword in keywords:
|
||||
color = "\033[95m"
|
||||
elif isnumber(tempword) or tempword == "true" or tempword == "false":
|
||||
color = "\033[96m"
|
||||
elif tempword[0] == "#":
|
||||
incom = True
|
||||
color = "\033[37m"
|
||||
elif tempword[0] == "&":
|
||||
color = "\033[93m"
|
||||
elif tempword[0] == "$":
|
||||
color = "\033[33m"
|
||||
elif tempword[0] == "%":
|
||||
color = "\033[32m"
|
||||
elif tempword[0] == "-":
|
||||
color = "\033[34m"
|
||||
elif tempword[0] == "!":
|
||||
color = "\033[94m"
|
||||
elif tempword[0] == "*":
|
||||
color = "\033[93m"
|
||||
elif tempword[0] == "\"":
|
||||
color = "\033[92m"
|
||||
instr = not instr
|
||||
elif tempword[0] == "\'" and tempword[len(tempword)-1] == "\'":
|
||||
color = "\033[92m"
|
||||
elif instr:
|
||||
color = "\033[92m"
|
||||
elif incom:
|
||||
color = "\033[37m"
|
||||
else:
|
||||
color = "\033[91m"
|
||||
|
||||
print(f'{color}{tempword}', end=" ")
|
||||
print()
|
||||
|
||||
print("\033[00m", end="")
|
@@ -110,6 +110,20 @@ Allows you to set a variable to a value.
|
||||
|
||||
Usage: `set &var $value`
|
||||
|
||||
#### gettype
|
||||
|
||||
Gets the type of a variable. Outputs a string which can be "int", "double", "bool", "string", "char".
|
||||
|
||||
Usage: `gettype $value &var`
|
||||
|
||||
#### exists
|
||||
|
||||
Checks if a variable exists with a direct reference. If the variable exists, outputs true. Otherwise outputs false.
|
||||
|
||||
Usage `exists &var1 &var2`
|
||||
|
||||
Note: You can also replace &var1 with a list or line reference to check if it also exists
|
||||
|
||||
#### setlist
|
||||
|
||||
Allows you to initialize a list.
|
||||
@@ -276,7 +290,7 @@ Usage: `call !function &var
|
||||
|
||||
#### 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. All functions from the library will be given a prefix, meaning functions will be registered as `!libName:functionName`.
|
||||
|
||||
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.
|
||||
|
||||
@@ -284,7 +298,7 @@ Usage: `use $stringvalue`
|
||||
|
||||
#### 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`. All functions from the library will be given a prefix, meaning functions will be registered as `!libName:functionName`.
|
||||
|
||||
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.
|
||||
|
||||
|
9
extlibs/compiling.md
Normal file
9
extlibs/compiling.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## Compiling External Libraries
|
||||
|
||||
On Linux:
|
||||
|
||||
`g++ -shared -fPIC -o filename.so filename.cpp`
|
||||
|
||||
On macOS:
|
||||
|
||||
`g++ -shared -fPIC -o filename.dylib filename.cpp`
|
9
extlibs/exec/README.md
Normal file
9
extlibs/exec/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# exec library
|
||||
|
||||
This library allows executing third party executables via the C++ "system" command.
|
||||
|
||||
## Functions
|
||||
|
||||
### fun -int !exec -string &command
|
||||
|
||||
Runs a command on a system. Returns the status code of that command.
|
16
extlibs/exec/exec.cpp
Normal file
16
extlibs/exec/exec.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "ground_lib.h"
|
||||
|
||||
GroundValue exec(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_STRING);
|
||||
int exec = system(GET_STRING(args[0]));
|
||||
return GROUND_INT_VAL(exec);
|
||||
}
|
||||
|
||||
GROUND_LIBRARY_INTERFACE()
|
||||
|
||||
GROUND_LIBRARY_INIT()
|
||||
REGISTER_GROUND_FUNCTION(exec);
|
||||
GROUND_LIBRARY_INIT_END()
|
||||
|
||||
GROUND_LIBRARY_CLEANUP()
|
||||
GROUND_LIBRARY_CLEANUP_END()
|
193
extlibs/exec/ground_lib.h
Normal file
193
extlibs/exec/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
|
13
extlibs/file/README.md
Normal file
13
extlibs/file/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# file library
|
||||
|
||||
This library allows reading from and writing to files on the system.
|
||||
|
||||
## Functions
|
||||
|
||||
### fun -string !readFile -string &fileName
|
||||
|
||||
This function reads all content from a file and returns it.
|
||||
|
||||
### fun -bool !writeFile -string &fileName -string &content
|
||||
|
||||
This function overwrites a file with specified content. If successful, returns true. If not successful, returns false, and prints out a reason why it didn't work.
|
193
extlibs/file/ground_lib.h
Normal file
193
extlibs/file/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/math/README.md
Normal file
45
extlibs/math/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# math library
|
||||
|
||||
This library adds extra math functions to Ground.
|
||||
|
||||
## Functions
|
||||
|
||||
### fun -double !sinVal -double &input
|
||||
|
||||
Gets the sin of input.
|
||||
|
||||
### fun -double !cosVal -double &input
|
||||
|
||||
Gets the cos of input.
|
||||
|
||||
### fun -double !tanVal -double &input
|
||||
|
||||
Gets the tan of input.
|
||||
|
||||
### fun -double !sqrtVal -double &input
|
||||
|
||||
Gets the square root of input.
|
||||
|
||||
### fun -int !modVal -int &a -int &b
|
||||
|
||||
Gets the remainder of a divided by b.
|
||||
|
||||
### fun -double !floorVal -double &input
|
||||
|
||||
Gets the floor of input.
|
||||
|
||||
### fun -double !ceilVal -double &input
|
||||
|
||||
Gets the ceil of input.
|
||||
|
||||
### fun -double !roundVal -double &input
|
||||
|
||||
Rounds the input to the nearest integer.
|
||||
|
||||
### fun -int !randomInt -int &min -int &max
|
||||
|
||||
Gets a random integer between min and max (inclusive).
|
||||
|
||||
### fun -double !randomDouble -double &min -double &max
|
||||
|
||||
Gets a random double between min and max (inclusive).
|
193
extlibs/math/ground_lib.h
Normal file
193
extlibs/math/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
|
79
extlibs/math/math.cpp
Normal file
79
extlibs/math/math.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "ground_lib.h"
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
|
||||
// Math functions
|
||||
GroundValue sinVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(sin(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue cosVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(cos(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue tanVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(tan(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue sqrtVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(sqrt(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue modVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_2(GROUND_INT, GROUND_INT);
|
||||
return GROUND_INT_VAL(GET_INT(args[0]) % GET_INT(args[1]));
|
||||
}
|
||||
|
||||
GroundValue floorVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(floor(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue ceilVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(ceil(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue roundVal(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_1(GROUND_DOUBLE);
|
||||
return GROUND_DOUBLE_VAL(round(GET_DOUBLE(args[0])));
|
||||
}
|
||||
|
||||
GroundValue randomInt(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_2(GROUND_INT, GROUND_INT);
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> distrib(GET_INT(args[0]), GET_INT(args[1]));
|
||||
return GROUND_INT_VAL(distrib(gen));
|
||||
}
|
||||
|
||||
GroundValue randomDouble(GroundValue* args, int arg_count) {
|
||||
VALIDATE_ARGS_2(GROUND_DOUBLE, GROUND_DOUBLE);
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> distrib(GET_DOUBLE(args[0]), GET_DOUBLE(args[1]));
|
||||
return GROUND_DOUBLE_VAL(distrib(gen));
|
||||
}
|
||||
|
||||
// Library setup
|
||||
GROUND_LIBRARY_INTERFACE()
|
||||
|
||||
GROUND_LIBRARY_INIT()
|
||||
REGISTER_GROUND_FUNCTION(sinVal);
|
||||
REGISTER_GROUND_FUNCTION(cosVal);
|
||||
REGISTER_GROUND_FUNCTION(tanVal);
|
||||
REGISTER_GROUND_FUNCTION(sqrtVal);
|
||||
REGISTER_GROUND_FUNCTION(modVal);
|
||||
REGISTER_GROUND_FUNCTION(floorVal);
|
||||
REGISTER_GROUND_FUNCTION(ceilVal);
|
||||
REGISTER_GROUND_FUNCTION(roundVal);
|
||||
REGISTER_GROUND_FUNCTION(randomInt);
|
||||
REGISTER_GROUND_FUNCTION(randomDouble);
|
||||
GROUND_LIBRARY_INIT_END()
|
||||
|
||||
GROUND_LIBRARY_CLEANUP()
|
||||
GROUND_LIBRARY_CLEANUP_END()
|
13
extlibs/request/README.md
Normal file
13
extlibs/request/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# request library
|
||||
|
||||
This library allows various web requests from within Ground.
|
||||
|
||||
## Functions
|
||||
|
||||
### fun -string !simpleRequest -string &url
|
||||
|
||||
Makes a web request to a URL, and returns the contents. If the request is not successful, returns a response beginning with "Error code", and prints it to the console.
|
||||
|
||||
### fun -bool !saveContents -string &url -string &location
|
||||
|
||||
Makes a web request to a URL, and saves the contents to a file. If successful, returns true. If not, returns false.
|
193
extlibs/request/ground_lib.h
Normal file
193
extlibs/request/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
|
@@ -4,12 +4,17 @@
|
||||
#include <cpr/interface.h>
|
||||
#include <fstream>
|
||||
|
||||
void error(std::string status) {
|
||||
std::cout << "Request error: " << status << std::endl;
|
||||
}
|
||||
|
||||
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)) {
|
||||
error("Non zero HTTP code " + std::to_string(r.status_code));
|
||||
return ground_string_val("Error code " + std::to_string(r.status_code));
|
||||
}
|
||||
|
||||
@@ -24,10 +29,12 @@ GroundValue saveContents(GroundValue* args, int arg_count) {
|
||||
if (file.good()) {
|
||||
cpr::Response r = cpr::Download(file, cpr::Url{GET_STRING(args[0])});
|
||||
|
||||
if (r.status_code >= 200 && r.status_code < 300) {
|
||||
if (!(r.status_code >= 200 && r.status_code < 300)) {
|
||||
error("Non zero HTTP code " + std::to_string(r.status_code));
|
||||
return GROUND_BOOL_VAL(false);
|
||||
}
|
||||
} else {
|
||||
error(std::string("Cannot open file ") + GET_STRING(args[1]) + " for writing");
|
||||
return GROUND_BOOL_VAL(false);
|
||||
}
|
||||
|
419
src/main.cpp
419
src/main.cpp
@@ -39,6 +39,7 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
@@ -72,7 +73,7 @@ enum class Instructions {
|
||||
Stdout, Stdin, Stdlnout,
|
||||
Add, Subtract, Multiply, Divide,
|
||||
Equal, Inequal, Greater, Lesser, Not,
|
||||
End, Set, Empty,
|
||||
End, Set, Empty, Gettype, Exists,
|
||||
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
|
||||
Getstrcharat, Getstrsize,
|
||||
Stoi, Stod, Tostring,
|
||||
@@ -95,24 +96,11 @@ enum class Instructions {
|
||||
See also parser function
|
||||
*/
|
||||
enum class Types {
|
||||
Int, Double, String, Char, Bool, Value, Direct, Line, ListRef, Label, Type, Function
|
||||
Int, Double, String, Char, Bool, Value, Direct, Line, List, ListRef, Label, Type, Function
|
||||
};
|
||||
|
||||
/*
|
||||
Literal struct
|
||||
Contains literal values. For example, if the following line was written:
|
||||
stdout "Hello world!"
|
||||
The Literal struct in the instruction should look like this:
|
||||
{
|
||||
val = "Hello world!"; // I am ignoring the variant for simplicity
|
||||
// of documenting the code
|
||||
}
|
||||
All value references are swapped out for their respective Literal they
|
||||
point to. See also variables map, parser function, interpreter function
|
||||
*/
|
||||
struct Literal {
|
||||
variant<int, double, bool, string, char> val;
|
||||
};
|
||||
// Forward declaration of Literal for list
|
||||
struct Literal;
|
||||
|
||||
/*
|
||||
List struct
|
||||
@@ -142,6 +130,22 @@ struct List {
|
||||
vector<Literal> val;
|
||||
};
|
||||
|
||||
/*
|
||||
Literal struct
|
||||
Contains literal values. For example, if the following line was written:
|
||||
stdout "Hello world!"
|
||||
The Literal struct in the instruction should look like this:
|
||||
{
|
||||
val = "Hello world!"; // I am ignoring the variant for simplicity
|
||||
// of documenting the code
|
||||
}
|
||||
All value references are swapped out for their respective Literal they
|
||||
point to. See also variables map, parser function, interpreter function
|
||||
*/
|
||||
struct Literal {
|
||||
variant<int, double, bool, string, char, List> val;
|
||||
};
|
||||
|
||||
/*
|
||||
Direct struct
|
||||
If the program being executed makes a direct reference, it is stored in a Direct
|
||||
@@ -164,6 +168,31 @@ struct FunctionRef {
|
||||
string fnName;
|
||||
};
|
||||
|
||||
/*
|
||||
Label struct
|
||||
Contains information needed to register labels
|
||||
*/
|
||||
struct Label {
|
||||
string id;
|
||||
int lineNum = -1;
|
||||
};
|
||||
|
||||
/*
|
||||
Line struct
|
||||
Contains information needed to jump to lines
|
||||
*/
|
||||
struct Line {
|
||||
int lineNum = -1;
|
||||
bool isLabel = false;
|
||||
string label;
|
||||
};
|
||||
|
||||
/*
|
||||
labelStack stack
|
||||
Allows each function to hold it's own set of labels
|
||||
*/
|
||||
stack<map<string, int>> labelStack;
|
||||
|
||||
/*
|
||||
ListRef struct
|
||||
Contains the name of a list referenced by the program. For example, if the
|
||||
@@ -184,19 +213,6 @@ struct ListRef {
|
||||
*/
|
||||
map<string, Literal> variables;
|
||||
|
||||
|
||||
/*
|
||||
lists map
|
||||
Contains all lists made while running the program. See also List struct.
|
||||
*/
|
||||
map<string, List> lists;
|
||||
|
||||
/*
|
||||
labels map
|
||||
Contains all labels made in the program, for ease of jumping around the code.
|
||||
*/
|
||||
map<string, int> labels;
|
||||
|
||||
/*
|
||||
ValueRef struct
|
||||
If the program being executed makes a value reference, it is stored in a ValueRef
|
||||
@@ -211,22 +227,6 @@ struct ValueRef {
|
||||
string varName;
|
||||
};
|
||||
|
||||
/*
|
||||
Line struct
|
||||
If the program being executed makes a line reference, it is stored in a Line
|
||||
struct. For example, if the following line was written:
|
||||
jump %10
|
||||
The Line struct in the instruction should look like this:
|
||||
{
|
||||
lineNum = 10;
|
||||
}
|
||||
*/
|
||||
struct Line {
|
||||
int lineNum;
|
||||
bool isLabel = false;
|
||||
string label;
|
||||
};
|
||||
|
||||
/*
|
||||
Instruction struct
|
||||
An instruction usually corresponds to a line in the program being interpreted.
|
||||
@@ -252,9 +252,13 @@ struct Line {
|
||||
See also: Instructions enum class, Literal struct, ValueRef struct, Direct struct,
|
||||
Line struct, exec function, parser function
|
||||
*/
|
||||
|
||||
typedef variant<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line> argument;
|
||||
struct Instruction {
|
||||
Instructions inst = Instructions::Empty;
|
||||
vector<variant<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line>> args;
|
||||
vector<argument> args;
|
||||
bool isLabel = false;
|
||||
Label label;
|
||||
};
|
||||
|
||||
struct FnArg {
|
||||
@@ -270,7 +274,7 @@ struct Function {
|
||||
Types returnType;
|
||||
vector<FnArg> args;
|
||||
vector<Instruction> instructions;
|
||||
map<string, int> localLabels;
|
||||
vector<Label> labels;
|
||||
};
|
||||
|
||||
// C-compatible enum and types for developing libraries for Ground in C
|
||||
@@ -313,6 +317,9 @@ map<string, void*> loadedLibraries;
|
||||
// Map of function name to function pointer
|
||||
map<string, void*> externalFunctions;
|
||||
|
||||
// Libraries currently imported
|
||||
vector<string> libraries;
|
||||
|
||||
// Conversion functions
|
||||
GroundValue literalToGroundValue(const Literal& lit) {
|
||||
GroundValue gv;
|
||||
@@ -434,7 +441,7 @@ bool isListRef(string in) {
|
||||
bool isType(string in) {
|
||||
if (in.size() >= 1 && in[0] == '-') {
|
||||
string type = in.substr(1);
|
||||
if (type == "string" || type == "char" || type == "bool" || type == "double" || type == "int") return true;
|
||||
if (type == "string" || type == "char" || type == "bool" || type == "double" || type == "int" || type == "list") return true;
|
||||
else return false;
|
||||
} else return false;
|
||||
}
|
||||
@@ -486,6 +493,9 @@ string procFnName = "";
|
||||
|
||||
bool inFunction = false;
|
||||
|
||||
// Stack of strings for keeping track of which thing we're importing
|
||||
stack<string> importing;
|
||||
|
||||
// Forward declaration for the call instruction and use instruction
|
||||
Literal exec(vector<Instruction> in);
|
||||
|
||||
@@ -495,6 +505,21 @@ vector<Instruction> parser(vector<vector<string>> in);
|
||||
// Forward declaration for the use instruction
|
||||
vector<vector<string>> lexer(string in);
|
||||
|
||||
void preProcessLabels(vector<Instruction> instructions) {
|
||||
map<string, int> labels;
|
||||
int definingFunction = 0;
|
||||
for (int i = 0; i < instructions.size(); i++) {
|
||||
if (instructions[i].isLabel && definingFunction == 0) {
|
||||
labels[instructions[i].label.id] = i;
|
||||
} else if (instructions[i].inst == Instructions::Fun) {
|
||||
definingFunction++;
|
||||
} else if (instructions[i].inst == Instructions::Endfun) {
|
||||
definingFunction--;
|
||||
}
|
||||
}
|
||||
labelStack.push(labels);
|
||||
}
|
||||
|
||||
/*
|
||||
exec function
|
||||
This function takes a list of instructions (see Instruction struct above and parser
|
||||
@@ -502,14 +527,6 @@ vector<vector<string>> lexer(string in);
|
||||
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) {
|
||||
@@ -533,12 +550,12 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
} else if (holds_alternative<Line>(l.args[j])) {
|
||||
Line ln = get<Line>(l.args[j]);
|
||||
if (ln.isLabel) {
|
||||
if (currentLabels->find(ln.label) != currentLabels->end()) {
|
||||
if (labelStack.top().find(ln.label) != labelStack.top().end()) {
|
||||
Line newLine;
|
||||
newLine.lineNum = (*currentLabels)[ln.label];
|
||||
newLine.lineNum = labelStack.top()[ln.label];
|
||||
l.args[j] = newLine;
|
||||
} else {
|
||||
error("Could not find label " + ln.label);
|
||||
if (l.inst != Instructions::Exists) error("Could not find label " + ln.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -653,7 +670,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
saves them to a list (vector) with name listName.
|
||||
*/
|
||||
case Instructions::Setlist:
|
||||
if (l.args.size() < 2) {
|
||||
if (l.args.size() < 1) {
|
||||
error("Could not find all arguments required for Setlist inbuilt");
|
||||
}
|
||||
{
|
||||
@@ -667,7 +684,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for (variant<Literal, ValueRef, ListRef, FunctionRef, TypeRef, Direct, Line> k : l.args) {
|
||||
for (argument k : l.args) {
|
||||
if (holds_alternative<Literal>(k)) {
|
||||
listContents.val.push_back(get<Literal>(k));
|
||||
} else {
|
||||
@@ -675,7 +692,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
lists[listName] = listContents;
|
||||
variables[listName].val = listContents;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -714,12 +731,16 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("Third argument of getlistat must be a direct reference");
|
||||
}
|
||||
|
||||
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];
|
||||
if (variables.find(listref.listName) != variables.end()) {
|
||||
if (holds_alternative<List>(variables[listref.listName].val)) {
|
||||
if (get<List>(variables[listref.listName].val).val.size() > ref) {
|
||||
bool existed = variables.count(var.varName) > 0;
|
||||
variables[var.varName] = get<List>(variables[listref.listName].val).val[ref];
|
||||
} else {
|
||||
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
|
||||
}
|
||||
} else {
|
||||
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
|
||||
error("Found a normal variable in place of a list");
|
||||
}
|
||||
} else {
|
||||
error("Unknown list: " + listref.listName);
|
||||
@@ -810,12 +831,15 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
} else {
|
||||
error("Third argument of setlistat must be a direct reference");
|
||||
}
|
||||
|
||||
if (lists.find(listref.listName) != lists.end()) {
|
||||
if (lists[listref.listName].val.size() > ref) {
|
||||
lists[listref.listName].val[ref] = value;
|
||||
} else {
|
||||
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
|
||||
if (variables.find(listref.listName) != variables.end()) {
|
||||
if (holds_alternative<List>(variables[listref.listName].val)) {
|
||||
if (get<List>(variables[listref.listName].val).val.size() > ref) {
|
||||
List tmpList = get<List>(variables[listref.listName].val);
|
||||
tmpList.val[ref] = value;
|
||||
variables[listref.listName].val = tmpList;
|
||||
} else {
|
||||
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error("Unknown list: " + listref.listName);
|
||||
@@ -837,7 +861,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
if (holds_alternative<ListRef>(l.args[0])) {
|
||||
listref = get<ListRef>(l.args[0]);
|
||||
} else {
|
||||
error("Second argument of listappend must be a list reference");
|
||||
error("First argument of listappend must be a list reference");
|
||||
}
|
||||
|
||||
if (holds_alternative<Literal>(l.args[1])) {
|
||||
@@ -846,8 +870,13 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("Second argument of listappend must be a direct reference");
|
||||
}
|
||||
|
||||
if (lists.find(listref.listName) != lists.end()) {
|
||||
lists[listref.listName].val.push_back(value);
|
||||
if (variables.find(listref.listName) != variables.end()) {
|
||||
if (!holds_alternative<List>(variables[listref.listName].val)) {
|
||||
error("Variable " + listref.listName + "is not a list");
|
||||
}
|
||||
List tmpList = get<List>(variables[listref.listName].val);
|
||||
tmpList.val.push_back(value);
|
||||
variables[listref.listName].val = tmpList;
|
||||
} else {
|
||||
error("Unknown list: " + listref.listName);
|
||||
}
|
||||
@@ -878,8 +907,8 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
|
||||
Literal newLit;
|
||||
if (lists.find(ref.listName) != lists.end()) {
|
||||
newLit.val = int(lists[ref.listName].val.size());
|
||||
if (variables.find(ref.listName) != variables.end()) {
|
||||
newLit.val = int(get<List>(variables[ref.listName].val).val.size());
|
||||
bool existed = variables.count(var.varName) > 0;
|
||||
variables[var.varName] = newLit;
|
||||
} else {
|
||||
@@ -1554,7 +1583,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("Could not find all arguments required for Jump inbuilt");
|
||||
}
|
||||
if (holds_alternative<Line>(l.args[0])) {
|
||||
i = get<Line>(l.args[0]).lineNum - 2;
|
||||
i = get<Line>(l.args[0]).lineNum - 1;
|
||||
} else {
|
||||
error("First argument of jump must be a line reference");
|
||||
}
|
||||
@@ -1580,7 +1609,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
if (isTrue) {
|
||||
if (holds_alternative<Line>(l.args[1])) {
|
||||
i = get<Line>(l.args[1]).lineNum - 2;
|
||||
i = get<Line>(l.args[1]).lineNum - 1;
|
||||
} else {
|
||||
error("Second argument of if must be a line reference");
|
||||
}
|
||||
@@ -1605,6 +1634,91 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("First argument of end must be an int value");
|
||||
}
|
||||
break;
|
||||
case Instructions::Gettype:
|
||||
if (l.args.size() < 2) {
|
||||
error("Could not find all arguments required for Gettype inbuilt");
|
||||
}
|
||||
{
|
||||
Literal val;
|
||||
|
||||
if (holds_alternative<Literal>(l.args[0])) {
|
||||
val = get<Literal>(l.args[0]);
|
||||
} else {
|
||||
error("First argument of gettype must be a literal");
|
||||
}
|
||||
|
||||
Types type = getLitType(val);
|
||||
|
||||
Direct ref;
|
||||
|
||||
if (holds_alternative<Direct>(l.args[1])) {
|
||||
ref = get<Direct>(l.args[1]);
|
||||
} else {
|
||||
error("Second argument of gettype must be a direct reference");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Types::Int:
|
||||
variables[ref.varName].val = "int";
|
||||
break;
|
||||
case Types::Double:
|
||||
variables[ref.varName].val = "double";
|
||||
break;
|
||||
case Types::Bool:
|
||||
variables[ref.varName].val = "bool";
|
||||
break;
|
||||
case Types::String:
|
||||
variables[ref.varName].val = "string";
|
||||
break;
|
||||
case Types::Char:
|
||||
variables[ref.varName].val = "char";
|
||||
break;
|
||||
default:
|
||||
error("Could not get type?? This should never be reached. Please report this issue");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
case Instructions::Exists:
|
||||
if (l.args.size() < 2) {
|
||||
error("Could not find all arguments required for Exists inbuilt");
|
||||
}
|
||||
{
|
||||
Direct ref2;
|
||||
if (holds_alternative<Direct>(l.args[1])) {
|
||||
ref2 = get<Direct>(l.args[1]);
|
||||
} else {
|
||||
error("Second argument of exists must be a direct reference");
|
||||
}
|
||||
|
||||
bool exists = false;
|
||||
if (holds_alternative<Direct>(l.args[0])) {
|
||||
if (variables.find(get<Direct>(l.args[0]).varName) != variables.end()) {
|
||||
exists = true;
|
||||
}
|
||||
} else if (holds_alternative<ListRef>(l.args[0])) {
|
||||
if (variables.find(get<ListRef>(l.args[0]).listName) != variables.end() && holds_alternative<List>(variables[get<ListRef>(l.args[0]).listName].val)) {
|
||||
exists = true;
|
||||
}
|
||||
} else if (holds_alternative<Line>(l.args[0])) {
|
||||
Line line = get<Line>(l.args[0]);
|
||||
if (line.isLabel) {
|
||||
if (labelStack.top().find(line.label) != labelStack.top().end()) {
|
||||
exists = true;
|
||||
}
|
||||
} else {
|
||||
if (line.lineNum > 0 && line.lineNum <= in.size()) {
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error("First argument of exists must be a direct, list, or line reference");
|
||||
}
|
||||
variables[ref2.varName].val = exists;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
fun instruction
|
||||
Allows functions to be defined.
|
||||
@@ -1630,6 +1744,10 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("Second argument of function must be a function reference");
|
||||
}
|
||||
|
||||
if (importing.size() > 0) {
|
||||
fnName = importing.top() + ":" + fnName;
|
||||
}
|
||||
|
||||
// Parse function arguments (type-direct pairs)
|
||||
if ((l.args.size() - 2) % 2 != 0) {
|
||||
error("Function arguments must be in type-direct pairs");
|
||||
@@ -1670,6 +1788,8 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
if (holds_alternative<Literal>(l.args[0])) {
|
||||
return get<Literal>(l.args[0]);
|
||||
} else if (holds_alternative<ListRef>(l.args[0])) {
|
||||
return variables[get<ListRef>(l.args[0]).listName];
|
||||
} else {
|
||||
error("First argument of return must be a literal value/value reference");
|
||||
}
|
||||
@@ -1691,8 +1811,10 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
if (holds_alternative<Literal>(l.args[0])) {
|
||||
fnArgs.push_back(get<Literal>(l.args[0]));
|
||||
} else if (holds_alternative<ListRef>(l.args[0])) {
|
||||
fnArgs.push_back(variables[get<ListRef>(l.args[0]).listName]);
|
||||
} else {
|
||||
error("First argument of pusharg must be a literal");
|
||||
error("First argument of pusharg must be a literal or list reference");
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@@ -1706,7 +1828,9 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
|
||||
FunctionRef ref;
|
||||
Direct returnRef;
|
||||
string returnRef;
|
||||
|
||||
bool expectList = true;
|
||||
|
||||
if (holds_alternative<FunctionRef>(l.args[0])) {
|
||||
ref = get<FunctionRef>(l.args[0]);
|
||||
@@ -1715,9 +1839,12 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
}
|
||||
|
||||
if (holds_alternative<Direct>(l.args[1])) {
|
||||
returnRef = get<Direct>(l.args[1]);
|
||||
returnRef = get<Direct>(l.args[1]).varName;
|
||||
} else if (holds_alternative<ListRef>(l.args[1])) {
|
||||
returnRef = get<ListRef>(l.args[1]).listName;
|
||||
expectList = true;
|
||||
} else {
|
||||
error("Second argument of call must be a direct reference");
|
||||
error("Second argument of call must be a direct reference or list reference");
|
||||
}
|
||||
|
||||
// Check for external function
|
||||
@@ -1740,7 +1867,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
|
||||
// Clear arguments and store result
|
||||
fnArgs.clear();
|
||||
variables[returnRef.varName] = resultLit;
|
||||
variables[returnRef] = resultLit;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1773,19 +1900,30 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
// Create the variable
|
||||
variables[arg.ref.varName] = fnArgs[m];
|
||||
}
|
||||
|
||||
// Clear function arguments for next call
|
||||
fnArgs.clear();
|
||||
|
||||
// Process labels
|
||||
preProcessLabels(functions[ref.fnName].instructions);
|
||||
|
||||
// Call the function
|
||||
Literal retVal = exec(functions[ref.fnName].instructions, true);
|
||||
|
||||
// Restore scope
|
||||
variables = scopeBackup;
|
||||
|
||||
// Clear function arguments for next call
|
||||
fnArgs.clear();
|
||||
labelStack.pop();
|
||||
|
||||
// Now, assign the return value in the current scope.
|
||||
bool existed = variables.count(returnRef.varName) > 0;
|
||||
variables[returnRef.varName] = retVal;
|
||||
if (expectList) {
|
||||
variables[returnRef] = retVal;
|
||||
} else {
|
||||
if (holds_alternative<List>(retVal.val)) {
|
||||
error("Expecting to output a normal literal to a normal literal");
|
||||
} else {
|
||||
variables[returnRef] = retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Instructions::Use:
|
||||
@@ -1805,6 +1943,17 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("First argument for use requires a string literal");
|
||||
}
|
||||
|
||||
string libName = get<string>(get<Literal>(l.args[0]).val);
|
||||
bool imported = false;
|
||||
for (string lib : libraries) {
|
||||
if (lib == libName) {
|
||||
imported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (imported) break;
|
||||
|
||||
string groundLibsDir = getenv("GROUND_LIBS");
|
||||
|
||||
if (filesystem::exists(useName)) {
|
||||
@@ -1821,7 +1970,13 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
while (getline(file, lns)) {
|
||||
in += lns += "\n";
|
||||
}
|
||||
Literal ret = exec(parser(lexer(in)), false); }
|
||||
importing.push(get<string>(get<Literal>(l.args[0]).val));
|
||||
vector<Instruction> parsed = parser(lexer(in));
|
||||
preProcessLabels(parsed);
|
||||
Literal ret = exec(parsed, false);
|
||||
importing.pop();
|
||||
libraries.push_back(libName);
|
||||
}
|
||||
break;
|
||||
case Instructions::Extern:
|
||||
if (l.args.size() < 1) {
|
||||
@@ -1840,6 +1995,16 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
error("First argument for extern requires a string literal");
|
||||
}
|
||||
|
||||
bool imported = false;
|
||||
for (string lib : libraries) {
|
||||
if (lib == libName) {
|
||||
imported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (imported) break;
|
||||
|
||||
// Add appropriate extension
|
||||
string fullLibName = libName;
|
||||
#ifdef _WIN32
|
||||
@@ -1911,7 +2076,7 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
for (int i = 0; functionNames[i] != nullptr; i++) {
|
||||
void* funcPtr = getFunction(functionNames[i]);
|
||||
if (funcPtr) {
|
||||
externalFunctions[string(functionNames[i])] = funcPtr;
|
||||
externalFunctions[libName + ":" + string(functionNames[i])] = funcPtr;
|
||||
functionCount++;
|
||||
} else {
|
||||
error("Failed to get function pointer for: " + string(functionNames[i]));
|
||||
@@ -1928,10 +2093,42 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (executingFunction) error("Reached end of function and no value returned");
|
||||
Literal retLiteral;
|
||||
return retLiteral;
|
||||
}
|
||||
|
||||
string interpretEscapeSequences(string input) {
|
||||
string output;
|
||||
for (size_t i = 0; i < input.length(); ++i) {
|
||||
if (input[i] == '\\' && i + 1 < input.length()) {
|
||||
char next = input[i + 1];
|
||||
switch (next) {
|
||||
case 'n': output += '\n'; break;
|
||||
case 't': output += '\t'; break;
|
||||
case 'r': output += '\r'; break;
|
||||
case 'b': output += '\b'; break;
|
||||
case 'f': output += '\f'; break;
|
||||
case 'a': output += '\a'; break;
|
||||
case 'v': output += '\v'; break;
|
||||
case '\\': output += '\\'; break;
|
||||
case '\'': output += '\''; break;
|
||||
case '\"': output += '\"'; break;
|
||||
case '0': output += '\0'; break;
|
||||
default:
|
||||
output += '\\';
|
||||
output += next;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
output += input[i];
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
lexer function
|
||||
This function takes a string (the user's program) and splits it into smaller chunks
|
||||
@@ -2009,20 +2206,17 @@ vector<vector<string>> lexer(string in) {
|
||||
}
|
||||
/*
|
||||
parser function
|
||||
This takes multiple lines of code (in the form of vector<vector<string>>)
|
||||
and turns that human-readable code into a list of instructions.
|
||||
Refer to the Instruction struct above for more information.
|
||||
and turns thructions[iat human-readable code into a list of instructions.
|
||||
Refer to insructions[it.label.lineNum = i;the Instruction struct above for more information.
|
||||
*/
|
||||
vector<Instruction> parser(vector<vector<string>> in) {
|
||||
vector<Instruction> out;
|
||||
int lineNum = 1;
|
||||
int functionInstructionIndex = 0;
|
||||
int lineNum = 0;
|
||||
|
||||
for (vector<string> lineTokens : in) {
|
||||
lineNum ++;
|
||||
Instruction newInst;
|
||||
if (lineTokens.empty()) {
|
||||
if (processingFunction) functionInstructionIndex ++;
|
||||
out.push_back(newInst);
|
||||
continue;
|
||||
};
|
||||
@@ -2032,11 +2226,8 @@ vector<Instruction> parser(vector<vector<string>> in) {
|
||||
if (firstInst) {
|
||||
firstInst = false;
|
||||
if (isLabel(i)) {
|
||||
if (processingFunction) {
|
||||
functions[procFnName].localLabels[i.substr(1)] = functionInstructionIndex;
|
||||
} else {
|
||||
labels[i.substr(1)] = lineNum - 1;
|
||||
}
|
||||
newInst.isLabel = true;
|
||||
newInst.label.id = i.substr(1);
|
||||
}
|
||||
else if (i == "stdin") newInst.inst = Instructions::Stdin;
|
||||
else if (i == "stdout") newInst.inst = Instructions::Stdout;
|
||||
@@ -2054,6 +2245,8 @@ vector<Instruction> parser(vector<vector<string>> in) {
|
||||
else if (i == "not") newInst.inst = Instructions::Not;
|
||||
else if (i == "end") newInst.inst = Instructions::End;
|
||||
else if (i == "set") newInst.inst = Instructions::Set;
|
||||
else if (i == "gettype") newInst.inst = Instructions::Gettype;
|
||||
else if (i == "exists") newInst.inst = Instructions::Exists;
|
||||
else if (i == "setlist") newInst.inst = Instructions::Setlist;
|
||||
else if (i == "setlistat") newInst.inst = Instructions::Setlistat;
|
||||
else if (i == "getlistat") newInst.inst = Instructions::Getlistat;
|
||||
@@ -2064,10 +2257,7 @@ 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;
|
||||
functionInstructionIndex = 0;
|
||||
}
|
||||
else if (i == "fun") newInst.inst = Instructions::Fun;
|
||||
else if (i == "return") newInst.inst = Instructions::Return;
|
||||
else if (i == "endfun") newInst.inst = Instructions::Endfun;
|
||||
else if (i == "pusharg") newInst.inst = Instructions::Pusharg;
|
||||
@@ -2104,6 +2294,7 @@ vector<Instruction> parser(vector<vector<string>> in) {
|
||||
else if (type == "double") newType.type = Types::Double;
|
||||
else if (type == "int") newType.type = Types::Int;
|
||||
else if (type == "bool") newType.type = Types::Bool;
|
||||
else if (type == "list") newType.type = Types::List;
|
||||
else error("Ground could not find type. This is an error with the interpreter, not your code. This line of code should never be reached.");
|
||||
newInst.args.push_back(newType);
|
||||
}
|
||||
@@ -2137,7 +2328,7 @@ vector<Instruction> parser(vector<vector<string>> in) {
|
||||
case Types::String:
|
||||
{
|
||||
Literal newLiteral;
|
||||
string str = i.substr(1, i.size() - 2);
|
||||
string str = interpretEscapeSequences(i.substr(1, i.size() - 2));
|
||||
newLiteral.val = str;
|
||||
newInst.args.push_back(newLiteral);
|
||||
}
|
||||
@@ -2174,10 +2365,12 @@ vector<Instruction> parser(vector<vector<string>> in) {
|
||||
newInst.args.push_back(newLiteral);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("This type should not be obtained in normal execution");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (processingFunction) functionInstructionIndex++;
|
||||
out.push_back(newInst);
|
||||
}
|
||||
return out;
|
||||
@@ -2202,7 +2395,7 @@ int main(int argc, char** argv) {
|
||||
lit.val = argv[i];
|
||||
argsList.val.push_back(lit);
|
||||
}
|
||||
lists["args"] = argsList;
|
||||
variables["args"].val = argsList;
|
||||
|
||||
ifstream file(argv[1]);
|
||||
string lns;
|
||||
@@ -2210,7 +2403,9 @@ int main(int argc, char** argv) {
|
||||
while (getline(file, lns)) {
|
||||
in += lns += "\n";
|
||||
}
|
||||
Literal ret = exec(parser(lexer(in)), false);
|
||||
vector<Instruction> parsed = parser(lexer(in));
|
||||
preProcessLabels(parsed);
|
||||
Literal ret = exec(parsed, false);
|
||||
if (holds_alternative<int>(ret.val)) {
|
||||
return get<int>(ret.val);
|
||||
} else {
|
||||
|
20
tests/exists.grnd
Normal file
20
tests/exists.grnd
Normal file
@@ -0,0 +1,20 @@
|
||||
set &testVar "dingus"
|
||||
exists &testVar &exist
|
||||
stdlnout $exist
|
||||
|
||||
setlist *myList "item"
|
||||
exists *myList &exist
|
||||
stdlnout $exist
|
||||
|
||||
@dingus
|
||||
exists %dingus &exist
|
||||
stdlnout $exist
|
||||
|
||||
exists &doesNotExist &exist
|
||||
stdlnout $exist
|
||||
|
||||
exists *doesNotExist &exist
|
||||
stdlnout $exist
|
||||
|
||||
exists %doesNotExist &exist
|
||||
stdlnout $exist
|
@@ -17,3 +17,25 @@ endfun
|
||||
call !jumpy &tmp
|
||||
|
||||
stdlnout "I called a function"
|
||||
|
||||
# This function returns a list
|
||||
|
||||
fun -list !dingus
|
||||
stdlnout "Testing lists in functions"
|
||||
setlist *dingle "heheheha" "hahahahe" "hmmm"
|
||||
return *dingle
|
||||
endfun
|
||||
|
||||
call !dingus *outlist
|
||||
|
||||
getlistsize *outlist &size
|
||||
set &counter 0
|
||||
@loopstart
|
||||
equal $size $counter &cond
|
||||
if $cond %loopend
|
||||
getlistat *outlist $counter &tmp
|
||||
stdlnout $tmp
|
||||
add 1 $counter &counter
|
||||
jump %loopstart
|
||||
@loopend
|
||||
end 0
|
||||
|
5
tests/gettype.grnd
Normal file
5
tests/gettype.grnd
Normal file
@@ -0,0 +1,5 @@
|
||||
set &myVar "dingus"
|
||||
|
||||
gettype $myVar &type
|
||||
|
||||
stdlnout $type
|
@@ -1,5 +1,5 @@
|
||||
use "library"
|
||||
|
||||
call !dingus &var
|
||||
call !library:dingus &var
|
||||
|
||||
stdlnout $var
|
||||
|
Reference in New Issue
Block a user