60 Commits

Author SHA1 Message Date
9e329968d1 Error types, print lists, better fn calling 2025-09-13 15:47:24 +10:00
e56c560514 Merge pull request 'Add Error Function' (#18) from DiamondNether90/ground_fork:master into master
Reviewed-on: max/ground#18
2025-09-12 12:05:05 +10:00
9cbe546e8a Error function 2025-09-12 11:57:45 +10:00
d9790711c6 Merge pull request 'Merge pull request 'Update docs/highlight.py' (#15) from DiamondNether90/ground_fork:master into master' (#1) from max/ground:master into master
Reviewed-on: https://git.maxwellj.xyz/DiamondNether90/ground_fork/pulls/1
2025-09-12 11:15:42 +10:00
310fede3ec Merge pull request 'Update docs/highlight.py' (#15) from DiamondNether90/ground_fork:master into master
Reviewed-on: https://git.maxwellj.xyz/max/ground/pulls/15
2025-09-02 20:42:39 +10:00
a4eba4ae47 Merge branch 'master' into master 2025-09-02 20:42:32 +10:00
e2a037befc Update docs/highlight.py
Fixed mishighlight for negative numbers
2025-09-02 08:38:54 +10:00
872392c1c5 Simple escape sequences 2025-09-01 13:10:46 +10:00
2e1e2e727b Merge pull request 'Update docs/highlight.py' (#13) from DiamondNether90/ground_fork:master into master
Reviewed-on: https://git.maxwellj.xyz/max/ground/pulls/13
2025-09-01 08:32:08 +10:00
38681f72d7 Merge branch 'master' into master 2025-09-01 08:31:46 +10:00
9c8cd58449 Update docs/highlight.py
Added file support, added inline comment support, added true/false support, fixed strings.
2025-09-01 08:30:21 +10:00
074b473bb1 Merge pull request 'Add Python Highlighting Script' (#12) from DiamondNether90/ground_fork:master into master
Reviewed-on: max/ground#12
2025-08-31 21:05:03 +10:00
a3b9cd2519 Add docs/highlight.py 2025-08-31 19:04:20 +10:00
76205a613d Rewrite label system 2025-08-31 15:04:27 +10:00
7961195018 Little thing to catch rouge functions 2025-08-30 16:28:27 +10:00
e73e5a7ebc Fix a couple small things 2025-08-30 14:20:34 +10:00
06ed44a010 Fix calling functions inside functions 2025-08-30 13:39:51 +10:00
d8cc3ff9e0 Functions can return lists 2025-08-30 13:05:28 +10:00
f32f76450a Lists are now stored in the variables map 2025-08-30 12:28:07 +10:00
cea66aa583 Only import libraries once 2025-08-30 11:12:53 +10:00
8d80416c5c Exists for lists and lines 2025-08-30 10:50:19 +10:00
0eb5670dfd Add exists instruction 2025-08-30 10:40:59 +10:00
8247ba36e4 gettype instruction 2025-08-30 10:24:31 +10:00
76e36b7ca3 Add a prefix for imported libraries 2025-08-30 10:06:25 +10:00
6596bfcc85 Organisation, documentation, mathlib 2025-08-28 11:11:59 +10:00
a9bfc1b0e3 exec external library 2025-08-25 20:17:41 +10:00
c952be1fe6 Command line arguments 2025-08-25 19:13:00 +10:00
1c5ca8d201 Simple file and request libraries 2025-08-25 18:29:45 +10:00
e56e6de911 Experimental external library support 2025-08-25 17:35:16 +10:00
38b17e7db5 Add library guide 2025-08-25 14:10:14 +10:00
e74e5ea548 Update syntax 2025-08-25 13:51:22 +10:00
f5140e3833 Delete docs/writing-a-program 2025-08-25 13:36:04 +10:00
c01dd470e1 Update docs/writing-a-program.md 2025-08-25 13:35:56 +10:00
16660c6a8d More reliable scoping 2025-08-25 13:35:22 +10:00
5bd8519517 External library support 2025-08-25 13:22:15 +10:00
2f706e2285 Update lines of code in readme 2025-08-25 11:29:43 +10:00
db99b9ac9f Fix getstrcharat error message 2025-08-25 11:22:02 +10:00
e906734aca Fix function jumping bug (I think) 2025-08-24 20:22:52 +10:00
8da5a2bf93 Experimental function jumping 2025-08-24 16:30:42 +10:00
e9600d8500 Scoping 2025-08-24 15:08:07 +10:00
1c0dfcc4b7 Fix not 2025-08-24 14:41:34 +10:00
f7f3972248 Function arguments, start of scoping 2025-08-22 13:52:26 +10:00
50d83aa228 "not" instruction 2025-08-21 11:05:32 +10:00
14758df1ab Example calculator 2025-08-21 08:43:22 +10:00
b19b4123d8 Parser string and character fix 2025-08-18 13:38:26 +10:00
28a9e389fa Basic function calling support 2025-08-18 09:36:35 +10:00
e4cc6b2f14 Fix critical bug, further functions 2025-08-15 11:35:58 +10:00
bb753e97d4 Return function 2025-08-13 18:31:54 +10:00
4cd4d9080d Function declarations 2025-08-13 09:40:03 +10:00
52eadaa9c3 Typerefs and functionrefs 2025-08-12 09:45:00 +10:00
db0c362efb Start work on functions 2025-08-11 14:57:45 +10:00
3a8600b481 Merge branch 'master' of https://git.maxwellj.xyz/max/ground 2025-08-11 14:13:00 +10:00
c39967a72f Type conversion 2025-08-11 14:12:25 +10:00
163f85b896 Fix a typo 2025-08-11 13:29:10 +10:00
09033cd432 More stuff 2025-08-11 10:07:05 +10:00
566d3aa0fb updates 2025-08-11 08:57:45 +10:00
f8397e85d4 Labels 2025-08-10 16:08:56 +10:00
3f2482d7ea Labels 2025-08-10 15:42:52 +10:00
2e388c6e68 Start work on lists 2025-08-10 13:31:28 +10:00
f43f79b869 Bugfix: no longer treats floats as ints 2025-08-09 19:52:49 +10:00
35 changed files with 3423 additions and 121 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
ground
Bobfile

View File

@@ -1,5 +1,5 @@
compiler "g++";
binary "ground";
source "src/main.cpp";
flag "O3";
flag "Ofast";
compile;

View File

@@ -8,7 +8,7 @@ Ground is an interpreter which processes and interprets Ground instructions. It
* **Simple syntax:** Ground doesn't have very many features, and that's intentional. It makes Ground easy to learn, and keeps it speedy.
* **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 761 lines of code (and 233 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?
@@ -16,7 +16,7 @@ Ground is an interpreter which processes and interprets Ground instructions. It
Clone the repo and compile with your favourite C++ compiler:
```
g++ src/main.cpp -std=C++17 -O3 -o ground
g++ src/main.cpp -std=c++17 -O3 -o ground
```
(You can omit the -std flag on systems which default to the latest standard, and the -O3 flag if you're fine with a slightly slower interpreter.)

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

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

70
docs/highlight.py Normal file
View 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", "error"]
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="")

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

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

View File

@@ -1,6 +1,6 @@
## Ground Syntax Guide
### General syntax
## General syntax
Ground uses simple instructions and arguments to run code.
@@ -32,12 +32,38 @@ Reference a line (a line reference) with a percent symbol before a line number:
jump %10
```
### Keywords
Alternatively, set a label:
```
@myLabel # The '@' symbol denotes setting a label
```
and jump to that (setting labels will be discussed below):
```
jump %myLabel
```
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.
Note: In most of these functions, if a direct reference is used, the value outputted by that function will be avaliable at that variable. Any existing value inside that variable will be overwritten.
### Control Flow
#### if
Make a decision based on a boolean. If the boolean is true, jumps to the line referenced.
@@ -54,7 +80,9 @@ Usage: `jump %1`
Ends the program. Requires an integer for a status code.
Usage: `end $value`
Usage: `end $intvalue`
### I/O
#### stdin
@@ -74,12 +102,74 @@ Allows output to the console, appending a new line at the end.
Usage: `stdlnout $value`
### Variables and Lists
#### set
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.
Usage: `setlist *list $value1 $value2 $value3...`
#### setlistat
Sets a list item at an index. The item at the index must already exist. Lists are index 0.
Usage: `setlistat *list $intvalue $value`
#### getlistat
Gets a list item at an index, and puts it in the variable provided. The item at the index must already exist. Lists are index 0.
Usage: `getlistat *list $intvalue &var`
#### getlistsize
Gets the size of a list and puts it in the variable provided.
Usage: `getlistsize *list &var`
#### listappend
Appends an item to a list.
Usage: `listappend *list $var`
### String Operations
#### getstrsize
Gets the size of a string and puts it in the variable provided.
Usage: `getstrsize $stringvalue &var`
#### getstrcharat
Gets a character at a certain position in a string and saves it to a variable.
Usage: `getstrcharat $stringvalue $intvalue &var`
### Maths
#### add
Adds two numbers. Numbers mean an integer or a double. Outputs to a direct reference.
@@ -104,6 +194,8 @@ Divides two numbers. Numbers mean an integer or a double. Outputs to a direct re
Usage: `divide $value $value &var`
### Comparisons
#### equal
Checks if two values are equal. Outputs a boolean to a direct reference.
@@ -116,6 +208,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.
@@ -127,3 +225,83 @@ Usage: `greater $value $value &var`
Checks if the left value is lesser than the right value. Outputs a boolean to a direct reference.
Usage: `lesser $value $value &var`
### Type Conversions
#### stoi
Converts a string to an integer. Throws an error if the string cannot be turned into an integer.
Usage: `stoi $stringvalue &var`
#### stod
Converts a string to a double. Throws an error if the string cannot be turned into a double.
Usage: `stod $stringvalue &var`
#### tostring
Converts any type to a string.
Usage: `tostring $value &var`
### Functions and function specific features (Experimental, please report bugs!)
Some symbols specific to this category:
* `!function`: A function reference
* `-type`: A type reference. Can be one of the following: "-string", "-char", "-int", "-double", "-bool"
#### fun
Defines a function. All code between `fun` and `endfun` will be included in the function.
Usage: `fun -type !functionname -type &var -type &var -type &var # and so on...`
Usage note: The first type specified before the function name must be the return type. The type displayed before all vars shows what type that variable must be.
#### return
Returns back to the main program (or other function that called this function). Also returns a value, which must be of the type defined when defining the function.
Usage: `return $value`
#### endfun
Ends a function definition. When a function reaches the end the argument list will be cleared.
Usage: `endfun`
#### pusharg
Adds a value to the argument list which will be passed to the function when it is called.
Usage: `pusharg $value`
#### call
Calls a function, with all the arguments in the argument list. The return value will be put in the specified variable.
Usage: `call !function &var
### Interacting with Libraries
#### 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. 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.
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`. 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.
Documentation on how to do external libraries coming soon.
Usage: `extern $stringvalue`

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

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

9
extlibs/compiling.md Normal file
View 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
View 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
View 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
View File

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

13
extlibs/file/README.md Normal file
View 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.

43
extlibs/file/file.cpp Normal file
View File

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

193
extlibs/file/ground_lib.h Normal file
View File

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

193
extlibs/ground_lib.h Normal file
View File

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

45
extlibs/math/README.md Normal file
View 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
View File

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

79
extlibs/math/math.cpp Normal file
View 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
View 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.

View File

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

View File

@@ -0,0 +1,52 @@
#include "ground_lib.h"
#include <cpr/cpr.h>
#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));
}
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)) {
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);
}
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()

File diff suppressed because it is too large Load Diff

12
tests/args.grnd Executable file
View File

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

1
tests/error.grnd Normal file
View File

@@ -0,0 +1 @@
error "Hello, world!" "sillyError"

89
tests/everything.grnd Normal file
View File

@@ -0,0 +1,89 @@
# I/O
stdlnout "Hello there!"
stdout "What is your name? "
stdin &name
add "Hello, " $name &out
stdlnout $out
# Types
stdlnout "dingus"
stdlnout 7
stdlnout 3.14159
stdlnout true
stdlnout 'e'
# Variables
set &testVar "This is a test"
stdlnout $testVar
# Lists
setlist *testList "Item 1" "Another Item" "Item the Third"
getlistat *testList 2 &tmp
stdlnout $tmp
setlistat *testList 1 "I changed this item"
getlistat *testList 1 &tmp
stdlnout $tmp
listappend *testList "I appended this item"
getlistat *testList 3 &tmp
stdlnout $tmp
getlistsize *testList &tmp
stdlnout $tmp
# String Operations
set &testStr "dingus"
getstrsize $testStr &tmp
stdlnout $tmp
getstrcharat $testStr 3 &tmp
stdlnout $tmp
# Maths
add 1 1 &tmp
stdlnout $tmp
subtract 10 5 &tmp
stdlnout $tmp
multiply 15 15 &tmp
stdlnout $tmp
divide 36 4 &tmp
stdlnout $tmp
# Comparisons
equal 5 5 &tmp
stdlnout $tmp
inequal 5 5 &tmp
stdlnout $tmp
greater 10 5 &tmp
stdlnout $tmp
lesser 10 5 &tmp
stdlnout $tmp
# Control flow
set &counter 0
@myLabel
add $counter 1 &counter
stdlnout $counter
inequal $counter 10 &case
if $case %myLabel
# That's it!
stdlnout "Everything ran! Check the output to see if it is what is expected."
end 0

20
tests/exists.grnd Normal file
View 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

41
tests/functions.grnd Normal file
View File

@@ -0,0 +1,41 @@
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
!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
View File

@@ -0,0 +1,5 @@
set &myVar "dingus"
gettype $myVar &type
stdlnout $type

View File

@@ -1,7 +1,9 @@
@begin
stdout "Do you like cheese? "
stdin &userin
equal $userin "yes" &condition
if $condition %7
if $condition %end
stdlnout "That is sad"
jump %1
jump %begin
@end
stdlnout "Awesome! I do too!"

14
tests/lists.grnd Normal file
View File

@@ -0,0 +1,14 @@
# A cool list
setlist *favWords "hello" "there" "general" "kenobi"
stdlnout *favWords
set &count 0
set &passedThrough true
@jmpbck
getlistat *favWords $count &tmp
stdlnout $tmp
add $count 1 &count
getlistsize *favWords &tmp2
inequal $count $tmp2 &tmp3
if $tmp3 %jmpbck

View File

@@ -1,5 +1,6 @@
set &var 0
@jump
add 1 $var &var
stdlnout $var
greater 1000 $var &cond
if $cond %2
greater 10000 $var &cond
if $cond %jump

10
tests/typeconvs.grnd Normal file
View File

@@ -0,0 +1,10 @@
stod "3.14" &out
stdlnout $out
stoi "732" &out
add 1 $out &out
stdlnout $out
tostring 3.14 &out
add $out " is a number of sorts" &out
stdlnout $out

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

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

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

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