forked from ground/ground
Compare commits
27 Commits
a4eba4ae47
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e5a39f07fb | |||
| fa5d805eef | |||
| 81d6e21a00 | |||
| 7066b4300d | |||
| 5ff3e8a86a | |||
| 9dbef7f8a8 | |||
| c0a7698497 | |||
| c4ebca9ed9 | |||
| bfbcd376df | |||
| 28faf6142c | |||
| 063cdc92e3 | |||
| 0a962b569a | |||
| d7b0c4d818 | |||
| 93eec33e60 | |||
| ab4b7e6aae | |||
| 39dc320f5d | |||
| 1147383ece | |||
| cc896629f7 | |||
| 2fd344af82 | |||
| 4fc76e99da | |||
| cdd1d32cee | |||
| 3495268672 | |||
| 9e329968d1 | |||
| e56c560514 | |||
| 9cbe546e8a | |||
| d9790711c6 | |||
| 310fede3ec |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
ground
|
||||
Bobfile
|
||||
build
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "libraries"]
|
||||
path = libraries
|
||||
url = https://chookspace.com/ground/libraries
|
||||
5
Bobfile
5
Bobfile
@@ -1,5 +0,0 @@
|
||||
compiler "g++";
|
||||
binary "ground";
|
||||
source "src/main.cpp";
|
||||
flag "Ofast";
|
||||
compile;
|
||||
22
Makefile
Normal file
22
Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -O3 -Isrc -Wall -Wextra -std=c++17
|
||||
|
||||
BUILD_DIR = build
|
||||
SRCS = $(shell find src -name '*.cpp')
|
||||
VPATH = $(sort $(dir $(SRCS)))
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(SRCS:.cpp=.o)))
|
||||
TARGET = ground
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
|
||||
|
||||
$(BUILD_DIR)/%.o: %.cpp
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(TARGET)
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -13,13 +13,7 @@ Ground is an interpreter which processes and interprets Ground instructions. It
|
||||
|
||||
## How do I get started?
|
||||
|
||||
Clone the repo and compile with your favourite C++ compiler:
|
||||
|
||||
```
|
||||
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.)
|
||||
Clone the repo and compile with `make`.
|
||||
|
||||
Run a Ground program:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ def isnumber(num):
|
||||
|
||||
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"]
|
||||
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> "
|
||||
|
||||
@@ -44,12 +44,6 @@ 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 `#`:
|
||||
|
||||
```
|
||||
@@ -84,23 +78,23 @@ Usage: `end $intvalue`
|
||||
|
||||
### I/O
|
||||
|
||||
#### stdin
|
||||
#### input (or stdin)
|
||||
|
||||
Allows input from the console.
|
||||
|
||||
Usage: `stdin &var`
|
||||
Usage: `input &var`
|
||||
|
||||
#### stdout
|
||||
#### print (or stdout)
|
||||
|
||||
Allows output to the console.
|
||||
|
||||
Usage: `stdout $value`
|
||||
Usage: `print $value`
|
||||
|
||||
#### stdlnout
|
||||
#### println (or stdlnout)
|
||||
|
||||
Allows output to the console, appending a new line at the end.
|
||||
|
||||
Usage: `stdlnout $value`
|
||||
Usage: `println $value`
|
||||
|
||||
### Variables and Lists
|
||||
|
||||
@@ -128,31 +122,31 @@ Note: You can also replace &var1 with a list or line reference to check if it al
|
||||
|
||||
Allows you to initialize a list.
|
||||
|
||||
Usage: `setlist *list $value1 $value2 $value3...`
|
||||
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`
|
||||
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`
|
||||
Usage: `getlistat &list $intvalue &var`
|
||||
|
||||
#### getlistsize
|
||||
|
||||
Gets the size of a list and puts it in the variable provided.
|
||||
|
||||
Usage: `getlistsize *list &var`
|
||||
Usage: `getlistsize &list &var`
|
||||
|
||||
#### listappend
|
||||
|
||||
Appends an item to a list.
|
||||
|
||||
Usage: `listappend *list $var`
|
||||
Usage: `listappend &list $var`
|
||||
|
||||
### String Operations
|
||||
|
||||
@@ -252,9 +246,9 @@ 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"
|
||||
* `-type`: A type reference. Can be one of the following: "-string", "-char", "-int", "-double", "-bool", or a custom struct (see "Custom data structures" below)
|
||||
|
||||
#### fun
|
||||
#### fun
|
||||
|
||||
Defines a function. All code between `fun` and `endfun` will be included in the function.
|
||||
|
||||
@@ -286,17 +280,44 @@ Calls a function, with all the arguments in the argument list. The return value
|
||||
|
||||
Usage: `call !function &var
|
||||
|
||||
### Custom Data Structures
|
||||
|
||||
#### struct
|
||||
|
||||
Begins creation of a custom data structure, which can contain values and functions. Similar to a class or struct in other programming languages.
|
||||
|
||||
In between the `struct` and `endstruct` lines, you can insert the following:
|
||||
|
||||
* `init` statements, to initialize values
|
||||
* `fun`/`endfun` statements, to add member functions to the struct
|
||||
|
||||
When calling a function from inside a struct, all members of the struct are avaliable to read from and write to.
|
||||
|
||||
Usage: `struct -structName`
|
||||
|
||||
#### endstruct
|
||||
|
||||
Ends creation of a custom data structure.
|
||||
|
||||
Usage: `endstruct`
|
||||
|
||||
#### init
|
||||
|
||||
Initialises a variable with the default value of a type.
|
||||
|
||||
Usage: `init &var -type`
|
||||
|
||||
### Interacting with Libraries
|
||||
|
||||
#### use (Experimental, please report bugs!)
|
||||
#### use
|
||||
|
||||
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`.
|
||||
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!)
|
||||
#### extern
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
349
docs/tutorial.md
Normal file
349
docs/tutorial.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# Ground Tutorial
|
||||
|
||||
Get up and running with Ground in no time!
|
||||
|
||||
## First Steps
|
||||
|
||||
First, create a file ending in `.grnd` and open it in your favourite text editor. Then, write the following:
|
||||
|
||||
```
|
||||
println "Hello, World!"
|
||||
```
|
||||
|
||||
(I think you know what this does.)
|
||||
|
||||
(If you don't, it prints something to the console, and adds a new line at the end.)
|
||||
|
||||
You can test it on the console using the Ground interpreter:
|
||||
|
||||
```bash
|
||||
ground myFile.grnd
|
||||
```
|
||||
|
||||
But that's boring! You want to be able to do more in Ground. So, let's ask the user for something. We can write:
|
||||
|
||||
```
|
||||
input &var
|
||||
println $var
|
||||
```
|
||||
|
||||
This will wait for the user to type something into the console and press enter, and will save it to the variable `var`. Then, we print out the variable `var`.
|
||||
|
||||
But how do variables work in Ground?
|
||||
|
||||
## Variables
|
||||
|
||||
In Ground, variables are used to store values. (Yeah, I know, very original.) However, variables work a bit differently. What you need to know right now are called "value references" and "direct references".
|
||||
|
||||
### Value References
|
||||
|
||||
A value reference gets the value of a variable, and inserts it into the statement currently being executed.
|
||||
|
||||
For example, let's assume I have a variable called `myVar`, and it has the value `"Hello from the variable!"`. If you want to access that value, you use a value reference. To use a value reference, write the variable name, but prefixed with the `$` sign.
|
||||
|
||||
Let's try printing out `myVar`:
|
||||
|
||||
```
|
||||
println $myVar
|
||||
```
|
||||
|
||||
Sidenote: Value references are inserted before each line of code is ran.
|
||||
|
||||
You should see "Hello from the variable!" on the console, assuming you defined `myVar` beforehand. But how do you define a variable?
|
||||
|
||||
### Direct References
|
||||
|
||||
Direct references allow you to write to a variable. To use one, prefix your desired variable name with an `&` symbol.
|
||||
|
||||
Ground works differently to many programming languages in that you can't write things like `myVar = 10`. Everything uses an instruction (which I'll explain in a moment). So in Ground, you'd write:
|
||||
|
||||
```
|
||||
set &myVar 10
|
||||
```
|
||||
|
||||
to set `myVar` to 10. Simple, right?
|
||||
|
||||
### Summary
|
||||
* Value references let you get the variable of a value, and are prefixed with `$`.
|
||||
* Direct references let you write to variables, and are prefixed with `&`.
|
||||
|
||||
As an example:
|
||||
|
||||
```
|
||||
set &myVar "Hello from the variable!"
|
||||
println $myVar
|
||||
```
|
||||
|
||||
will set `myVar` to "Hello from the variable!", and print it to the console.
|
||||
|
||||
## Instructions
|
||||
|
||||
Now, I assume you want to do more than setting variables and printing them, right? Luckily for you, Ground has (at the time of writing) 35 instructions to build whatever you can think of (not to mention libraries, but that's a later topic).
|
||||
|
||||
First, here's how you use them:
|
||||
|
||||
### Using instructions
|
||||
|
||||
Each line of Ground code contains an instruction and arguments. For example:
|
||||
|
||||
```
|
||||
add 9 10 &result
|
||||
```
|
||||
|
||||
This uses the `add` keyword (which adds two things together) to add 9 and 10, and outputs the result (which would be 19) to the direct reference provided (`result`).
|
||||
|
||||
So each instruction works sort of like a function in most other languages.
|
||||
|
||||
Now you know how they work, here's a list of cool instructions:
|
||||
|
||||
### Cool instructions
|
||||
|
||||
So here's a list of some instructions keywords to know about at present:
|
||||
|
||||
### Mathy stuff
|
||||
|
||||
`add`, `subtract`, `multiply`, `divide`
|
||||
|
||||
These instructions all take two numbers, and operate on them (add adds, subtract subtracts, and so on), and then output the result to the provided direct reference. You can use them all like:
|
||||
|
||||
```
|
||||
add 5 6 &addition
|
||||
```
|
||||
|
||||
```
|
||||
multiply 10 30 &myVar
|
||||
```
|
||||
|
||||
(and so on.)
|
||||
|
||||
Tip: `add` works on strings for concatenation! (for those who don't know, a string is a series of characters surrounded with `"`'s)
|
||||
|
||||
### User interaction
|
||||
|
||||
`input` (or `stdin`)
|
||||
|
||||
This instruction takes input from the console, and saves it to the given direct reference. For example:
|
||||
|
||||
```
|
||||
input &myVar
|
||||
```
|
||||
|
||||
`print` (or `stdout`) and `println` (or `stdlnout`)
|
||||
|
||||
These instructions print out values to the console. `println` also prints a new line character after your statement. For example:
|
||||
|
||||
```
|
||||
println "Hi there! This has a new line at the end"
|
||||
print "Heyo! This doesn't have a new line at the end."
|
||||
```
|
||||
|
||||
### Other variable stuff
|
||||
|
||||
`set`
|
||||
|
||||
Sets a variable to something.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
set &myVar "Hi there!"
|
||||
```
|
||||
|
||||
`gettype`
|
||||
|
||||
Gets the type of a variable, in the form of a string. As an example:
|
||||
|
||||
```
|
||||
gettype $someSortOfValue &type
|
||||
```
|
||||
|
||||
If you accessed `$type`, you'd get one of the following:
|
||||
|
||||
"int", "double", "bool", "string", "char".
|
||||
|
||||
More on types later!
|
||||
|
||||
## Commenting
|
||||
|
||||
Comments are done with a `#`. Anything after the `#` on the line is ignored.
|
||||
|
||||
## Control Flow
|
||||
|
||||
Now, I assume you want to be able to use logic in your programs, right? Ground simplifies control flow to the bare minimum.
|
||||
|
||||
### Labels
|
||||
|
||||
A label is a point in your Ground code you can jump to. To set one, instead of writing an instruction, you can do something like this:
|
||||
|
||||
```
|
||||
@myLabel
|
||||
```
|
||||
|
||||
This will allow you to jump back to that point in code at any time.
|
||||
|
||||
### Jumping around
|
||||
|
||||
Jump to labels with the `jump` instruction.
|
||||
|
||||
```
|
||||
# Infinite loop!
|
||||
@myLabel
|
||||
jump %myLabel
|
||||
```
|
||||
|
||||
But what does `%myLabel` mean? This is the third reference type: a line reference. It tells Ground which line to look at.
|
||||
|
||||
You can also use `if`, however `if` works differently:
|
||||
|
||||
```
|
||||
@myLabel
|
||||
if $myCondition %myLabel
|
||||
```
|
||||
|
||||
This essentialy says "if `myCondition` is true, then jump to `myLabel`". But how do you compute conditions?
|
||||
|
||||
### More instructions!
|
||||
|
||||
Here are some useful instructions to compute a condition:
|
||||
|
||||
`equal`, `inequal`
|
||||
|
||||
These compare two values, and puts a boolean in a variable once they're compared.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# Prints out true
|
||||
equal "Hello!" "Hello!" &condition
|
||||
println $condition
|
||||
|
||||
# Prints out false
|
||||
equal 10 20 &condition
|
||||
println $condition
|
||||
|
||||
# Prints out true
|
||||
inequal "Hi there!" "Hello there!" &condition
|
||||
println $condition
|
||||
```
|
||||
|
||||
`equal` and `inequal` work on most regular values.
|
||||
|
||||
`greater`, `lesser`
|
||||
|
||||
These instructions check if the first provided value is greater or lesser than the second provided value. As an example:
|
||||
|
||||
```
|
||||
# Prints out true
|
||||
greater 10 5 &condition
|
||||
println $condition
|
||||
```
|
||||
|
||||
### So how do I use these?
|
||||
|
||||
Labels, `if`, and `jump` can be used to create what would be if and while statements in other languages. Here's a loop that counts to 10:
|
||||
|
||||
```
|
||||
set &counter 0
|
||||
|
||||
@loopStart
|
||||
|
||||
# Add one to our counter and print it out
|
||||
add $counter 1 &counter
|
||||
println $counter
|
||||
|
||||
# Check if we've hit 10 yet
|
||||
equal $counter 10 &condition
|
||||
|
||||
# If we have, go to the end of the loop
|
||||
if $condition %loopEnd
|
||||
|
||||
# Otherwise, go back to the start
|
||||
jump %loopStart
|
||||
@loopEnd
|
||||
```
|
||||
|
||||
And here's a conditional if statement that checks if a user guessed the right password:
|
||||
|
||||
```
|
||||
# Ask the user what the password is
|
||||
print "Password: "
|
||||
input &password
|
||||
|
||||
# Check if they got it right
|
||||
equal $password "supersecurepassword" &condition
|
||||
if $condition %rightPassword
|
||||
jump %wrongPassword
|
||||
|
||||
@rightPassword
|
||||
println "Correct!"
|
||||
jump %end
|
||||
|
||||
@wrongPassword
|
||||
println "Incorrect!"
|
||||
jump %end
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
## Building a meaningful program
|
||||
|
||||
Now we have all the building blocks to create a simple program!
|
||||
|
||||
Let's write a program that loops until the user tells us the right answer to a question. Here's how we do it:
|
||||
|
||||
First let's create a label so we can loop back to the start of our program:
|
||||
|
||||
```
|
||||
@begin
|
||||
```
|
||||
|
||||
Then we ask the user for their answer:
|
||||
|
||||
```
|
||||
print "Do you like cheese?"
|
||||
input &userInput
|
||||
```
|
||||
|
||||
After this, we can check for the desired answer:
|
||||
```
|
||||
equal $userInput "yes" &condition
|
||||
if $condition %success
|
||||
```
|
||||
|
||||
Now, if the user answers "yes" to our question, they will be sent to the `%success` label. But we need to handle what happens if they don't say what we want them to.
|
||||
|
||||
So we can tell them that they said the wrong thing, and jump back to the start of the program.
|
||||
|
||||
```
|
||||
println "That's a pity"
|
||||
jump %begin
|
||||
```
|
||||
|
||||
At last, we should handle what happens when we get the input we want:
|
||||
|
||||
```
|
||||
@success
|
||||
println "Yay!"
|
||||
```
|
||||
|
||||
Try running that program!
|
||||
|
||||
Here's the full text of what we wrote:
|
||||
|
||||
```
|
||||
@begin
|
||||
print "Do you like cheese?"
|
||||
input &userInput
|
||||
equal $userInput "yes" &condition
|
||||
if $condition %success
|
||||
|
||||
println "That's a pity"
|
||||
jump %begin
|
||||
|
||||
@success
|
||||
println "Yay!"
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now you've completed the basic tutorial! This will probably be expanded in future, but for now you can look at [the syntax guide](https://chookspace.com/ground/ground/src/branch/master/docs/syntax.md) for more features to use.
|
||||
@@ -1,9 +0,0 @@
|
||||
## Compiling External Libraries
|
||||
|
||||
On Linux:
|
||||
|
||||
`g++ -shared -fPIC -o filename.so filename.cpp`
|
||||
|
||||
On macOS:
|
||||
|
||||
`g++ -shared -fPIC -o filename.dylib filename.cpp`
|
||||
@@ -1,9 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,16 +0,0 @@
|
||||
#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()
|
||||
@@ -1,193 +0,0 @@
|
||||
#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
|
||||
@@ -1,13 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,43 +0,0 @@
|
||||
#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()
|
||||
@@ -1,193 +0,0 @@
|
||||
#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
|
||||
@@ -1,193 +0,0 @@
|
||||
#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
|
||||
@@ -1,45 +0,0 @@
|
||||
# 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).
|
||||
@@ -1,193 +0,0 @@
|
||||
#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
|
||||
@@ -1,79 +0,0 @@
|
||||
#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()
|
||||
@@ -1,13 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,193 +0,0 @@
|
||||
#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
|
||||
@@ -1,52 +0,0 @@
|
||||
#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()
|
||||
1
libraries
Submodule
1
libraries
Submodule
Submodule libraries added at 5e7de482e7
244
src/data/data.cpp
Normal file
244
src/data/data.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "data.h"
|
||||
#include "../main.h"
|
||||
#include "../error/error.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
/*
|
||||
labelStack stack
|
||||
Allows each function to hold it's own set of labels
|
||||
*/
|
||||
std::stack<std::map<std::string, int>> labelStack;
|
||||
|
||||
/*
|
||||
variables map
|
||||
Contains all variables made while running the program. See also Literal struct.
|
||||
*/
|
||||
std::map<std::string, Literal> variables;
|
||||
|
||||
/*
|
||||
functions map
|
||||
Contains the code of functions and types of their values
|
||||
*/
|
||||
std::map<std::string, Function> functions;
|
||||
|
||||
/*
|
||||
structs map
|
||||
Contains structs (see the Struct struct for more info)
|
||||
*/
|
||||
std::map<std::string, Struct> structs;
|
||||
|
||||
/*
|
||||
fnArgs vector
|
||||
Containst the arguments to be passed to a function
|
||||
*/
|
||||
std::vector<Literal> fnArgs;
|
||||
|
||||
// External library functions and other things
|
||||
|
||||
// Handle to loaded libraries
|
||||
std::map<std::string, void*> loadedLibraries;
|
||||
|
||||
// Map of function name to function pointer
|
||||
std::map<std::string, void*> externalFunctions;
|
||||
|
||||
// Libraries currently imported
|
||||
std::vector<std::string> libraries;
|
||||
|
||||
// Conversion functions
|
||||
GroundValue literalToGroundValue(const Literal& lit) {
|
||||
GroundValue gv;
|
||||
if (std::holds_alternative<int>(lit.val)) {
|
||||
gv.type = GROUND_INT;
|
||||
gv.data.int_val = std::get<int>(lit.val);
|
||||
} else if (std::holds_alternative<double>(lit.val)) {
|
||||
gv.type = GROUND_DOUBLE;
|
||||
gv.data.double_val = std::get<double>(lit.val);
|
||||
} else if (std::holds_alternative<bool>(lit.val)) {
|
||||
gv.type = GROUND_BOOL;
|
||||
gv.data.bool_val = std::get<bool>(lit.val) ? 1 : 0;
|
||||
} else if (std::holds_alternative<std::string>(lit.val)) {
|
||||
gv.type = GROUND_STRING;
|
||||
gv.data.string_val = const_cast<char*>(std::get<std::string>(lit.val).c_str());
|
||||
} else if (std::holds_alternative<char>(lit.val)) {
|
||||
gv.type = GROUND_CHAR;
|
||||
gv.data.char_val = std::get<char>(lit.val);
|
||||
}
|
||||
return gv;
|
||||
}
|
||||
|
||||
/*
|
||||
catches stackmap
|
||||
This stores all catches in a scope, to catch errors before they happen.
|
||||
*/
|
||||
std::stack<std::map<std::string, Direct>> catches;
|
||||
|
||||
Literal groundValueToLiteral(const GroundValue& gv) {
|
||||
Literal lit;
|
||||
switch (gv.type) {
|
||||
case GROUND_INT:
|
||||
lit.val = gv.data.int_val;
|
||||
break;
|
||||
case GROUND_DOUBLE:
|
||||
lit.val = gv.data.double_val;
|
||||
break;
|
||||
case GROUND_BOOL:
|
||||
lit.val = (gv.data.bool_val != 0);
|
||||
break;
|
||||
case GROUND_STRING:
|
||||
lit.val = std::string(gv.data.string_val);
|
||||
break;
|
||||
case GROUND_CHAR:
|
||||
lit.val = gv.data.char_val;
|
||||
break;
|
||||
}
|
||||
return lit;
|
||||
}
|
||||
|
||||
/*
|
||||
is* functions
|
||||
These functions determine if a string value can be converted into a different type.
|
||||
*/
|
||||
|
||||
bool isInt(std::string in) {
|
||||
try {
|
||||
std::stoi(in);
|
||||
if (std::stod(in) != std::stoi(in)) return false;
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isDouble(std::string in) {
|
||||
try {
|
||||
std::stod(in);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isBool(std::string in) {
|
||||
if (in == "true" || in == "false") return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isString(std::string in) {
|
||||
if (in.size() >= 2 && in[0] == '"' && in[in.size() - 1] == '"') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isChar(std::string in) {
|
||||
if (in.size() == 3 && in[0] == '\'' && in[in.size() - 1] == '\'') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isValue(std::string in) {
|
||||
if (in.size() >= 1 && in[0] == '$') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isDirect(std::string in) {
|
||||
if (in.size() >= 1 && in[0] == '&') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isLine(std::string in) {
|
||||
if (in.size() >= 1 && in[0] == '%') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isLabel(std::string in) {
|
||||
if (in.size() >= 1 && in[0] == '@') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isType(std::string in) {
|
||||
if (in.size() > 1 && in[0] == '-') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isFunction(std::string in) {
|
||||
if (in.size() >= 1 && in[0] == '!') return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool isReferencingStruct(std::string in) {
|
||||
return in.find('.') != std::string::npos;
|
||||
}
|
||||
|
||||
/*
|
||||
getType function
|
||||
This function determines the type of a value inside a string based on the is*
|
||||
functions above. Returns a type from the Types enum class.
|
||||
*/
|
||||
Types getType(std::string in) {
|
||||
if (isInt(in)) return Types::Int;
|
||||
if (isDouble(in)) return Types::Double;
|
||||
if (isBool(in)) return Types::Bool;
|
||||
if (isString(in)) return Types::String;
|
||||
if (isChar(in)) return Types::Char;
|
||||
if (isValue(in)) return Types::Value;
|
||||
if (isDirect(in)) return Types::Direct;
|
||||
if (isLine(in)) return Types::Line;
|
||||
if (isLabel(in)) return Types::Label;
|
||||
if (isType(in)) return Types::Type;
|
||||
if (isFunction(in)) return Types::Function;
|
||||
error("Could not determine type of \"" + in + "\"");
|
||||
return Types::Int;
|
||||
}
|
||||
|
||||
/*
|
||||
getLitType function
|
||||
This function determines the type of a value inside a Literal based on the
|
||||
holds_alternative() function. Returns a type from the Types enum class.
|
||||
*/
|
||||
Types getLitType(Literal in) {
|
||||
if (std::holds_alternative<int>(in.val)) return Types::Int;
|
||||
if (std::holds_alternative<double>(in.val)) return Types::Double;
|
||||
if (std::holds_alternative<bool>(in.val)) return Types::Bool;
|
||||
if (std::holds_alternative<std::string>(in.val)) return Types::String;
|
||||
if (std::holds_alternative<char>(in.val)) return Types::Char;
|
||||
if (std::holds_alternative<List>(in.val)) return Types::List;
|
||||
return Types::Other;
|
||||
}
|
||||
|
||||
/*
|
||||
setVal function
|
||||
This function sets the value of a variable (whether in a struct or not).
|
||||
*/
|
||||
void setVal(std::string varName, Literal value) {
|
||||
if (isReferencingStruct(varName)) {
|
||||
std::string structName;
|
||||
std::string varInStruct;
|
||||
|
||||
size_t dotPos = varName.find('.'); // Use size_t
|
||||
if (dotPos != std::string::npos) {
|
||||
structName = varName.substr(0, dotPos);
|
||||
varInStruct = varName.substr(dotPos + 1);
|
||||
if (variables.find(structName) != variables.end()) {
|
||||
if (std::holds_alternative<Struct>(variables[structName].val)) {
|
||||
Struct structVal = std::get<Struct>(variables[structName].val);
|
||||
if (structVal.values.find(varInStruct) != structVal.values.end()) {
|
||||
structVal.values[varInStruct] = value;
|
||||
variables[structName].val = structVal; // Write back the modified struct
|
||||
} else {
|
||||
error("Could not find property '" + varInStruct + "' in struct '" + structName + "'");
|
||||
}
|
||||
} else {
|
||||
error("Variable '" + structName + "' is not a struct");
|
||||
}
|
||||
} else {
|
||||
error("Could not find struct '" + structName + "'");
|
||||
}
|
||||
} else {
|
||||
error("Invalid struct member access syntax");
|
||||
}
|
||||
} else {
|
||||
// Handle regular variables (both existing and new)
|
||||
variables[varName] = value;
|
||||
}
|
||||
}
|
||||
66
src/data/data.h
Normal file
66
src/data/data.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include "../main.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
/*
|
||||
functions map
|
||||
Contains the code of functions and types of their values
|
||||
*/
|
||||
extern std::map<std::string, Function> functions;
|
||||
|
||||
/*
|
||||
structs map
|
||||
Contains structs (see the Struct struct for more info)
|
||||
*/
|
||||
extern std::map<std::string, Struct> structs;
|
||||
|
||||
/*
|
||||
fnArgs vector
|
||||
Containst the arguments to be passed to a function
|
||||
*/
|
||||
extern std::vector<Literal> fnArgs;
|
||||
|
||||
// External library functions and other things
|
||||
|
||||
// Handle to loaded libraries
|
||||
extern std::map<std::string, void*> loadedLibraries;
|
||||
|
||||
// Map of function name to function pointer
|
||||
extern std::map<std::string, void*> externalFunctions;
|
||||
|
||||
// Libraries currently imported
|
||||
extern std::vector<std::string> libraries;
|
||||
|
||||
// catches stackmap
|
||||
extern std::stack<std::map<std::string, Direct>> catches;
|
||||
|
||||
// labelStack stack
|
||||
extern std::stack<std::map<std::string, int>> labelStack;
|
||||
|
||||
// variables map
|
||||
extern std::map<std::string, Literal> variables;
|
||||
|
||||
GroundValue literalToGroundValue(const Literal& lit);
|
||||
Literal groundValueToLiteral(const GroundValue& gv);
|
||||
void setVal(std::string varName, Literal value);
|
||||
|
||||
bool isInt(std::string in);
|
||||
bool isDouble(std::string in);
|
||||
bool isBool(std::string in);
|
||||
bool isString(std::string in);
|
||||
bool isChar(std::string in);
|
||||
bool isValue(std::string in);
|
||||
bool isDirect(std::string in);
|
||||
bool isLine(std::string in);
|
||||
bool isLabel(std::string in);
|
||||
bool isType(std::string in);
|
||||
bool isFunction(std::string in);
|
||||
bool isReferencingStruct(std::string in);
|
||||
|
||||
Types getType(std::string in);
|
||||
Types getLitType(Literal in);
|
||||
|
||||
void setVal(std::string varName, Literal value);
|
||||
34
src/error/error.cpp
Normal file
34
src/error/error.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include "error.h"
|
||||
#include "../data/data.h"
|
||||
#include "../main.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
error function
|
||||
Takes a string (which is a debug message) and prints it to the console, letting the
|
||||
user know what went wrong with the program.
|
||||
*/
|
||||
Literal error(string in, string errCode, int exitc) {
|
||||
Error retError;
|
||||
retError.code = errCode;
|
||||
retError.pops = 0;
|
||||
while (catches.size() > 0) {
|
||||
if (catches.top().find(errCode) != catches.top().end()) {
|
||||
Literal tmpLit;
|
||||
tmpLit.val = false;
|
||||
setVal(catches.top()[errCode].varName, tmpLit);
|
||||
retError.reporter = catches.top()[errCode].varName;
|
||||
Literal tmpLit2;
|
||||
tmpLit2.val = retError;
|
||||
return tmpLit2;
|
||||
} else {
|
||||
catches.pop();
|
||||
retError.pops ++;
|
||||
}
|
||||
}
|
||||
cout << "\033[31m" + errCode + ": \033[39m" << in << endl;
|
||||
exit(exitc);
|
||||
}
|
||||
6
src/error/error.h
Normal file
6
src/error/error.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../main.h"
|
||||
#include <string>
|
||||
|
||||
Literal error(std::string in, std::string errCode = "syntaxError", int exitc = 1);
|
||||
2003
src/interpreter/interpreter.cpp
Normal file
2003
src/interpreter/interpreter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
src/interpreter/interpreter.h
Normal file
7
src/interpreter/interpreter.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "../main.h"
|
||||
|
||||
void preProcessLabels(std::vector<Instruction> instructions);
|
||||
Literal exec(std::vector<Instruction> in, bool executingFunction = false);
|
||||
81
src/lexer/lexer.cpp
Normal file
81
src/lexer/lexer.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "lexer.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
lexer function
|
||||
This function takes the raw text from the file and splits it into a format
|
||||
that the parser can understand.
|
||||
*/
|
||||
|
||||
vector<vector<string>> lexer(string in) {
|
||||
vector<vector<string>> out;
|
||||
vector<string> line;
|
||||
string buf;
|
||||
bool procString = false;
|
||||
bool procChar = false;
|
||||
bool isComment = false;
|
||||
|
||||
for (char i : in) {
|
||||
switch (i) {
|
||||
case '"':
|
||||
if (!isComment) {
|
||||
if (procChar) {
|
||||
buf.push_back(i);
|
||||
} else {
|
||||
procString = !procString;
|
||||
buf.push_back(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
if (!isComment) {
|
||||
if (procString) {
|
||||
buf.push_back(i);
|
||||
} else {
|
||||
procChar = !procChar;
|
||||
buf.push_back(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
if (!procString && !procChar) {
|
||||
if (!buf.empty()) line.push_back(buf);
|
||||
out.push_back(line);
|
||||
buf.clear();
|
||||
line.clear();
|
||||
isComment = false;
|
||||
} else {
|
||||
if (!isComment) buf.push_back(i);
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
if (!procString && !procChar) {
|
||||
isComment = true;
|
||||
if (!buf.empty()) line.push_back(buf);
|
||||
out.push_back(line);
|
||||
buf.clear();
|
||||
line.clear();
|
||||
} else {
|
||||
buf.push_back(i);
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
if (!procString && !procChar) {
|
||||
if (!buf.empty() && !isComment) line.push_back(buf);
|
||||
buf.clear();
|
||||
} else {
|
||||
buf.push_back(i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!isComment) buf.push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!buf.empty()) line.push_back(buf);
|
||||
out.push_back(line);
|
||||
return out;
|
||||
}
|
||||
6
src/lexer/lexer.h
Normal file
6
src/lexer/lexer.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
std::vector<std::vector<std::string>> lexer(std::string in);
|
||||
2388
src/main.cpp
2388
src/main.cpp
File diff suppressed because it is too large
Load Diff
250
src/main.h
Normal file
250
src/main.h
Normal file
@@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
|
||||
// Headers for external libraries
|
||||
#ifdef _WIN32
|
||||
// Note: Windows support is experiemental. Maybe try using a superior
|
||||
// operating system? (cough cough, Linux?)
|
||||
#include <windows.h>
|
||||
#define DLOPEN(path) LoadLibrary(path)
|
||||
#define DLSYM(handle, name) GetProcAddress(handle, name)
|
||||
#define DLCLOSE(handle) FreeLibrary(handle)
|
||||
#define DLERROR() "Windows DLL Error"
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#define DLOPEN(path) dlopen(path, RTLD_LAZY)
|
||||
#define DLSYM(handle, name) dlsym(handle, name)
|
||||
#define DLCLOSE(handle) dlclose(handle)
|
||||
#define DLERROR() dlerror()
|
||||
#endif
|
||||
|
||||
/*
|
||||
Instructions enum class
|
||||
For each function keyword, an instruction is assigned. See also parser
|
||||
function, interpreter function, Instruction struct
|
||||
*/
|
||||
enum class Instructions {
|
||||
Jump, If,
|
||||
Stdout, Stdin, Stdlnout,
|
||||
Add, Subtract, Multiply, Divide,
|
||||
Equal, Inequal, Greater, Lesser, Not,
|
||||
End, Set, Empty, Gettype, Exists,
|
||||
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
|
||||
Getstrcharat, Getstrsize,
|
||||
Stoi, Stod, Tostring,
|
||||
Fun, Return, Endfun, Pusharg, Call, Local,
|
||||
Use, Extern, Error, Catch, Try, Exception,
|
||||
Struct, Endstruct, Init
|
||||
};
|
||||
|
||||
/*
|
||||
Types enum class
|
||||
Assists in type checking in the parser function. For example, the
|
||||
following values correspond to the following types:
|
||||
1 Int
|
||||
3.14 Double
|
||||
"Hello!" String
|
||||
'e' Char
|
||||
true Bool
|
||||
$value Value
|
||||
&var Direct
|
||||
%10 Line
|
||||
See also parser function
|
||||
*/
|
||||
enum class Types {
|
||||
Int, Double, String, Char, Bool, Value, Direct, Line, List, Label, Type, Function, Other
|
||||
};
|
||||
|
||||
// Forward declaration of Literal for list
|
||||
struct Literal;
|
||||
|
||||
/*
|
||||
List struct
|
||||
Contains literal values inside a vector. For example, if the following
|
||||
program was written:
|
||||
setlist #myNums 3 5 9 13
|
||||
The List struct which would be created and stored should look like this:
|
||||
{
|
||||
val = {
|
||||
Literal {
|
||||
val = 3
|
||||
},
|
||||
Literal {
|
||||
val = 5
|
||||
},
|
||||
Literal {
|
||||
val = 9
|
||||
},
|
||||
Literal {
|
||||
val = 13
|
||||
},
|
||||
}
|
||||
}
|
||||
All elements in the list must be of the same type. See also Literal struct.
|
||||
*/
|
||||
struct List {
|
||||
std::vector<Literal> val;
|
||||
};
|
||||
|
||||
/*
|
||||
Direct struct
|
||||
If the program being executed makes a direct reference, it is stored in a Direct
|
||||
struct. For example, if the following line was written:
|
||||
stdin &myVar
|
||||
The Direct struct in the instruction should look like this:
|
||||
{
|
||||
varName = "myVar";
|
||||
}
|
||||
*/
|
||||
struct Direct {
|
||||
std::string varName;
|
||||
};
|
||||
|
||||
struct TypeRef {
|
||||
bool isCustomType = false;
|
||||
std::string customType;
|
||||
Types type;
|
||||
};
|
||||
|
||||
struct FunctionRef {
|
||||
std::string fnName;
|
||||
};
|
||||
|
||||
/*
|
||||
Label struct
|
||||
Contains information needed to register labels
|
||||
*/
|
||||
struct Label {
|
||||
std::string id;
|
||||
int lineNum = -1;
|
||||
};
|
||||
|
||||
/*
|
||||
Line struct
|
||||
Contains information needed to jump to lines
|
||||
*/
|
||||
struct Line {
|
||||
int lineNum = -1;
|
||||
bool isLabel = false;
|
||||
std::string label;
|
||||
};
|
||||
|
||||
/*
|
||||
ValueRef struct
|
||||
If the program being executed makes a value reference, it is stored in a ValueRef
|
||||
struct. For example, if the following line was written:
|
||||
stdin &myVar
|
||||
The ValueRef struct in the instruction should look like this:
|
||||
{
|
||||
varName = "myVar";
|
||||
}
|
||||
*/
|
||||
struct ValueRef {
|
||||
std::string varName;
|
||||
};
|
||||
|
||||
/*
|
||||
Instruction struct
|
||||
An instruction usually corresponds to a line in the program being interpreted.
|
||||
For example, if the following line was written:
|
||||
add 5 $myVar &outVar
|
||||
The instruction should look like this:
|
||||
{
|
||||
inst = Instructions::Add;
|
||||
args = {
|
||||
Literal {
|
||||
val = 5
|
||||
},
|
||||
ValueRef {
|
||||
varName = "myVar"
|
||||
},
|
||||
Direct {
|
||||
varName = "outVar"
|
||||
}
|
||||
};
|
||||
}
|
||||
inst starts as empty, so empty lines and commented out lines do not get in the
|
||||
way of jump and if.
|
||||
See also: Instructions enum class, Literal struct, ValueRef struct, Direct struct,
|
||||
Line struct, exec function, parser function
|
||||
*/
|
||||
|
||||
typedef std::variant<Literal, ValueRef, FunctionRef, TypeRef, Direct, Line> argument;
|
||||
struct Instruction {
|
||||
Instructions inst = Instructions::Empty;
|
||||
std::vector<argument> args;
|
||||
bool isLabel = false;
|
||||
Label label;
|
||||
};
|
||||
|
||||
struct FnArg {
|
||||
Direct ref;
|
||||
Types type;
|
||||
};
|
||||
|
||||
/*
|
||||
Function struct
|
||||
Contains information needed to run a Ground function.
|
||||
*/
|
||||
struct Function {
|
||||
Types returnType;
|
||||
std::vector<FnArg> args;
|
||||
std::vector<Instruction> instructions;
|
||||
std::vector<Label> labels;
|
||||
};
|
||||
|
||||
/*
|
||||
Struct struct
|
||||
This struct stores data for structures in Ground.
|
||||
*/
|
||||
struct Struct {
|
||||
std::map<std::string, Literal> values;
|
||||
std::map<std::string, Function> functions;
|
||||
};
|
||||
|
||||
struct Error {
|
||||
std::string code;
|
||||
int pops;
|
||||
std::string reporter;
|
||||
};
|
||||
|
||||
/*
|
||||
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 {
|
||||
std::variant<int, double, bool, std::string, char, List, Struct, Error> val;
|
||||
};
|
||||
|
||||
// C-compatible enum and types for developing libraries for Ground in C
|
||||
typedef enum {
|
||||
GROUND_INT,
|
||||
GROUND_DOUBLE,
|
||||
GROUND_BOOL,
|
||||
GROUND_STRING,
|
||||
GROUND_CHAR
|
||||
} GroundType;
|
||||
|
||||
typedef struct {
|
||||
GroundType type;
|
||||
union {
|
||||
int int_val;
|
||||
double double_val;
|
||||
int bool_val;
|
||||
char* string_val;
|
||||
char char_val;
|
||||
} data;
|
||||
} GroundValue;
|
||||
|
||||
184
src/parser/parser.cpp
Normal file
184
src/parser/parser.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "parser.h"
|
||||
#include "../main.h"
|
||||
#include "../data/data.h"
|
||||
#include "../error/error.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
parser function
|
||||
This function takes a vector of a vector of strings from the lexer and
|
||||
converts it into a list of instructions that the interpreter can understand.
|
||||
*/
|
||||
vector<Instruction> parser(vector<vector<string>> in) {
|
||||
vector<Instruction> out;
|
||||
for (vector<string> line : in) {
|
||||
Instruction currentInstruction;
|
||||
if (line.size() < 1) {
|
||||
out.push_back(currentInstruction);
|
||||
continue;
|
||||
}
|
||||
string inst = line[0];
|
||||
if (isLabel(inst)) {
|
||||
currentInstruction.isLabel = true;
|
||||
currentInstruction.label.id = inst.substr(1);
|
||||
line.erase(line.begin());
|
||||
}
|
||||
else if (isFunction(inst)) {
|
||||
currentInstruction.inst = Instructions::Call;
|
||||
FunctionRef newFnRef;
|
||||
newFnRef.fnName = inst.substr(1);
|
||||
currentInstruction.args.push_back(newFnRef);
|
||||
}
|
||||
else if (inst == "stdout") currentInstruction.inst = Instructions::Stdout;
|
||||
else if (inst == "print") currentInstruction.inst = Instructions::Stdout;
|
||||
else if (inst == "stdlnout") currentInstruction.inst = Instructions::Stdlnout;
|
||||
else if (inst == "println") currentInstruction.inst = Instructions::Stdlnout;
|
||||
else if (inst == "stdin") currentInstruction.inst = Instructions::Stdin;
|
||||
else if (inst == "input") currentInstruction.inst = Instructions::Stdin;
|
||||
else if (inst == "add") currentInstruction.inst = Instructions::Add;
|
||||
else if (inst == "subtract") currentInstruction.inst = Instructions::Subtract;
|
||||
else if (inst == "multiply") currentInstruction.inst = Instructions::Multiply;
|
||||
else if (inst == "divide") currentInstruction.inst = Instructions::Divide;
|
||||
else if (inst == "set") currentInstruction.inst = Instructions::Set;
|
||||
else if (inst == "end") currentInstruction.inst = Instructions::End;
|
||||
else if (inst == "jump") currentInstruction.inst = Instructions::Jump;
|
||||
else if (inst == "if") currentInstruction.inst = Instructions::If;
|
||||
else if (inst == "equal") currentInstruction.inst = Instructions::Equal;
|
||||
else if (inst == "inequal") currentInstruction.inst = Instructions::Inequal;
|
||||
else if (inst == "greater") currentInstruction.inst = Instructions::Greater;
|
||||
else if (inst == "lesser") currentInstruction.inst = Instructions::Lesser;
|
||||
else if (inst == "not") currentInstruction.inst = Instructions::Not;
|
||||
else if (inst == "setlist") currentInstruction.inst = Instructions::Setlist;
|
||||
else if (inst == "getlistat") currentInstruction.inst = Instructions::Getlistat;
|
||||
else if (inst == "setlistat") currentInstruction.inst = Instructions::Setlistat;
|
||||
else if (inst == "getlistsize") currentInstruction.inst = Instructions::Getlistsize;
|
||||
else if (inst == "stoi") currentInstruction.inst = Instructions::Stoi;
|
||||
else if (inst == "stod") currentInstruction.inst = Instructions::Stod;
|
||||
else if (inst == "tostring") currentInstruction.inst = Instructions::Tostring;
|
||||
else if (inst == "fun") currentInstruction.inst = Instructions::Fun;
|
||||
else if (inst == "return") currentInstruction.inst = Instructions::Return;
|
||||
else if (inst == "endfun") currentInstruction.inst = Instructions::Endfun;
|
||||
else if (inst == "pusharg") currentInstruction.inst = Instructions::Pusharg;
|
||||
else if (inst == "call") currentInstruction.inst = Instructions::Call;
|
||||
else if (inst == "use") currentInstruction.inst = Instructions::Use;
|
||||
else if (inst == "extern") currentInstruction.inst = Instructions::Extern;
|
||||
else if (inst == "getstrcharat") currentInstruction.inst = Instructions::Getstrcharat;
|
||||
else if (inst == "getstrsize") currentInstruction.inst = Instructions::Getstrsize;
|
||||
else if (inst == "error") currentInstruction.inst = Instructions::Error;
|
||||
else if (inst == "catch") currentInstruction.inst = Instructions::Catch;
|
||||
else if (inst == "try") currentInstruction.inst = Instructions::Try;
|
||||
else if (inst == "exception") currentInstruction.inst = Instructions::Exception;
|
||||
else if (inst == "gettype") currentInstruction.inst = Instructions::Gettype;
|
||||
else if (inst == "exists") currentInstruction.inst = Instructions::Exists;
|
||||
else if (inst == "listappend") currentInstruction.inst = Instructions::Listappend;
|
||||
else if (inst == "listprepend") currentInstruction.inst = Instructions::Listprepend;
|
||||
else if (inst == "struct") currentInstruction.inst = Instructions::Struct;
|
||||
else if (inst == "endstruct") currentInstruction.inst = Instructions::Endstruct;
|
||||
else if (inst == "init") currentInstruction.inst = Instructions::Init;
|
||||
else {
|
||||
error("Unknown instruction " + inst);
|
||||
}
|
||||
|
||||
for (int i = 1; i < line.size(); i++) {
|
||||
string arg = line[i];
|
||||
Types type = getType(arg);
|
||||
switch (type) {
|
||||
case Types::Int:
|
||||
{
|
||||
Literal newLit;
|
||||
newLit.val = stoi(arg);
|
||||
currentInstruction.args.push_back(newLit);
|
||||
}
|
||||
break;
|
||||
case Types::Double:
|
||||
{
|
||||
Literal newLit;
|
||||
newLit.val = stod(arg);
|
||||
currentInstruction.args.push_back(newLit);
|
||||
}
|
||||
break;
|
||||
case Types::String:
|
||||
{
|
||||
Literal newLit;
|
||||
newLit.val = arg.substr(1, arg.size() - 2);
|
||||
currentInstruction.args.push_back(newLit);
|
||||
}
|
||||
break;
|
||||
case Types::Char:
|
||||
{
|
||||
Literal newLit;
|
||||
newLit.val = arg[1];
|
||||
currentInstruction.args.push_back(newLit);
|
||||
}
|
||||
break;
|
||||
case Types::Bool:
|
||||
{
|
||||
Literal newLit;
|
||||
if (arg == "true") newLit.val = true;
|
||||
else newLit.val = false;
|
||||
currentInstruction.args.push_back(newLit);
|
||||
}
|
||||
break;
|
||||
case Types::Value:
|
||||
{
|
||||
ValueRef newVal;
|
||||
newVal.varName = arg.substr(1);
|
||||
currentInstruction.args.push_back(newVal);
|
||||
}
|
||||
break;
|
||||
case Types::Direct:
|
||||
{
|
||||
Direct newDir;
|
||||
newDir.varName = arg.substr(1);
|
||||
currentInstruction.args.push_back(newDir);
|
||||
}
|
||||
break;
|
||||
case Types::Line:
|
||||
{
|
||||
Line newLine;
|
||||
string lineRef = arg.substr(1);
|
||||
if (isInt(lineRef)) {
|
||||
newLine.lineNum = stoi(lineRef);
|
||||
} else {
|
||||
newLine.isLabel = true;
|
||||
newLine.label = lineRef;
|
||||
}
|
||||
currentInstruction.args.push_back(newLine);
|
||||
}
|
||||
break;
|
||||
case Types::Type:
|
||||
{
|
||||
TypeRef newTypeRef;
|
||||
string type = arg.substr(1);
|
||||
if (type == "int") newTypeRef.type = Types::Int;
|
||||
else if (type == "double") newTypeRef.type = Types::Double;
|
||||
else if (type == "string") newTypeRef.type = Types::String;
|
||||
else if (type == "char") newTypeRef.type = Types::Char;
|
||||
else if (type == "bool") newTypeRef.type = Types::Bool;
|
||||
else if (type == "list") newTypeRef.type = Types::List;
|
||||
else {
|
||||
newTypeRef.isCustomType = true;
|
||||
newTypeRef.customType = type;
|
||||
};
|
||||
currentInstruction.args.push_back(newTypeRef);
|
||||
}
|
||||
break;
|
||||
case Types::Function:
|
||||
{
|
||||
FunctionRef newFuncRef;
|
||||
newFuncRef.fnName = arg.substr(1);
|
||||
currentInstruction.args.push_back(newFuncRef);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("Could not parse argument " + arg);
|
||||
}
|
||||
}
|
||||
out.push_back(currentInstruction);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
7
src/parser/parser.h
Normal file
7
src/parser/parser.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../main.h"
|
||||
|
||||
std::vector<Instruction> parser(std::vector<std::vector<std::string>> in);
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env ground
|
||||
stdlnout "Program args: "
|
||||
getlistsize *args &argsSize
|
||||
getlistsize &args &argsSize
|
||||
set &counter 0
|
||||
@loopstart
|
||||
equal $counter $argsSize &bool
|
||||
if $bool %end
|
||||
getlistat *args $counter &item
|
||||
getlistat &args $counter &item
|
||||
stdlnout $item
|
||||
add 1 $counter &counter
|
||||
jump %loopstart
|
||||
|
||||
23
tests/error.grnd
Normal file
23
tests/error.grnd
Normal file
@@ -0,0 +1,23 @@
|
||||
fun -int !divten -int &divisor
|
||||
divide 10 $divisor &out
|
||||
# Skip the rest of the function because we have an error
|
||||
println "aw yeag we devided by zero"
|
||||
return $out
|
||||
endfun
|
||||
|
||||
fun -string !wrapperFn -int &dingle
|
||||
pusharg $dingle
|
||||
!divten &result
|
||||
# Skip the rest of the function because we have an error
|
||||
println "big error incoming"
|
||||
tostring $result &out
|
||||
return $out
|
||||
endfun
|
||||
|
||||
catch "divisionByZeroError" &success
|
||||
pusharg 0
|
||||
!wrapperFn &result
|
||||
|
||||
# There's a catch in this scope, stop here and continue execution
|
||||
|
||||
println $success
|
||||
@@ -20,19 +20,19 @@ stdlnout $testVar
|
||||
|
||||
# Lists
|
||||
|
||||
setlist *testList "Item 1" "Another Item" "Item the Third"
|
||||
getlistat *testList 2 &tmp
|
||||
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
|
||||
setlistat &testList 1 "I changed this item"
|
||||
getlistat &testList 1 &tmp
|
||||
stdlnout $tmp
|
||||
|
||||
listappend *testList "I appended this item"
|
||||
getlistat *testList 3 &tmp
|
||||
listappend &testList "I appended this item"
|
||||
getlistat &testList 3 &tmp
|
||||
stdlnout $tmp
|
||||
|
||||
getlistsize *testList &tmp
|
||||
getlistsize &testList &tmp
|
||||
stdlnout $tmp
|
||||
|
||||
# String Operations
|
||||
|
||||
@@ -2,8 +2,8 @@ set &testVar "dingus"
|
||||
exists &testVar &exist
|
||||
stdlnout $exist
|
||||
|
||||
setlist *myList "item"
|
||||
exists *myList &exist
|
||||
setlist &myList "item"
|
||||
exists &myList &exist
|
||||
stdlnout $exist
|
||||
|
||||
@dingus
|
||||
@@ -13,8 +13,5 @@ stdlnout $exist
|
||||
exists &doesNotExist &exist
|
||||
stdlnout $exist
|
||||
|
||||
exists *doesNotExist &exist
|
||||
stdlnout $exist
|
||||
|
||||
exists %doesNotExist &exist
|
||||
stdlnout $exist
|
||||
|
||||
@@ -14,7 +14,7 @@ fun -bool !jumpy
|
||||
return true
|
||||
endfun
|
||||
|
||||
call !jumpy &tmp
|
||||
!jumpy &tmp
|
||||
|
||||
stdlnout "I called a function"
|
||||
|
||||
@@ -22,20 +22,28 @@ stdlnout "I called a function"
|
||||
|
||||
fun -list !dingus
|
||||
stdlnout "Testing lists in functions"
|
||||
setlist *dingle "heheheha" "hahahahe" "hmmm"
|
||||
return *dingle
|
||||
setlist &dingle "heheheha" "hahahahe" "hmmm"
|
||||
return $dingle
|
||||
endfun
|
||||
|
||||
call !dingus *outlist
|
||||
call !dingus &outlist
|
||||
|
||||
getlistsize *outlist &size
|
||||
getlistsize &outlist &size
|
||||
set &counter 0
|
||||
@loopstart
|
||||
equal $size $counter &cond
|
||||
if $cond %loopend
|
||||
getlistat *outlist $counter &tmp
|
||||
getlistat &outlist $counter &tmp
|
||||
stdlnout $tmp
|
||||
add 1 $counter &counter
|
||||
jump %loopstart
|
||||
@loopend
|
||||
end 0
|
||||
|
||||
fun -int !doSomething -int &a -string &b
|
||||
println $a
|
||||
println $b
|
||||
return 0
|
||||
endfun
|
||||
|
||||
pusharg 432 "dingle"
|
||||
!doSomething &ret
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# A cool list
|
||||
setlist *favWords "hello" "there" "general" "kenobi"
|
||||
setlist &favWords "hello" "there" "general" "kenobi"
|
||||
stdlnout $favWords
|
||||
|
||||
set &count 0
|
||||
set &passedThrough true
|
||||
|
||||
@jmpbck
|
||||
getlistat *favWords $count &tmp
|
||||
getlistat &favWords $count &tmp
|
||||
stdlnout $tmp
|
||||
add $count 1 &count
|
||||
getlistsize *favWords &tmp2
|
||||
getlistsize &favWords &tmp2
|
||||
inequal $count $tmp2 &tmp3
|
||||
if $tmp3 %jmpbck
|
||||
|
||||
29
tests/struct.grnd
Normal file
29
tests/struct.grnd
Normal file
@@ -0,0 +1,29 @@
|
||||
struct -point
|
||||
init &xpos -int
|
||||
init &ypos -int
|
||||
|
||||
fun -int !init -int &x -int &y
|
||||
set &xpos $x
|
||||
set &ypos $y
|
||||
return 0
|
||||
endfun
|
||||
|
||||
fun -string !toString
|
||||
tostring $xpos &xposstr
|
||||
tostring $ypos &yposstr
|
||||
add "x: " $xposstr &str
|
||||
add $str ", y: " &str
|
||||
add $str $yposstr &str
|
||||
return $str
|
||||
endfun
|
||||
endstruct
|
||||
|
||||
init &myPoint -point
|
||||
|
||||
pusharg 30 15
|
||||
!myPoint.init &out
|
||||
println $myPoint.xpos
|
||||
|
||||
!myPoint.toString &out
|
||||
|
||||
println $out
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user