82 Commits

Author SHA1 Message Date
e5a39f07fb Update .gitignore 2025-10-13 09:17:54 +11:00
fa5d805eef Refactoring (MAY BE BUGGY) 2025-10-13 09:16:28 +11:00
81d6e21a00 Deprecate *list syntax, use &direct refs instead 2025-09-25 08:09:27 +10:00
7066b4300d Fix "init &var -list" 2025-09-22 20:03:35 +10:00
5ff3e8a86a Update libraries 2025-09-21 16:04:56 +10:00
9dbef7f8a8 Add .gitmodules (libraries) 2025-09-21 15:51:27 +10:00
c0a7698497 Ints are autotransformed to doubles where needed 2025-09-21 14:29:59 +10:00
c4ebca9ed9 Catching errors across scopes 2025-09-21 14:10:09 +10:00
bfbcd376df Returning errors 2025-09-21 13:47:43 +10:00
28faf6142c basic version of catch 2025-09-21 08:55:50 +10:00
063cdc92e3 Re-add not instruction I accidentally deleted 2025-09-21 08:12:10 +10:00
0a962b569a Update syntax.md 2025-09-20 20:44:37 +10:00
d7b0c4d818 Function calling in structs 2025-09-20 20:23:57 +10:00
93eec33e60 Struct value modification 2025-09-20 18:41:22 +10:00
ab4b7e6aae Access variables in structs 2025-09-20 17:44:50 +10:00
39dc320f5d Type rework, start struct work 2025-09-20 15:17:22 +10:00
1147383ece pusharg takes multiple args at same time 2025-09-20 12:57:03 +10:00
cc896629f7 print, println, input, ground tutorial 2025-09-20 12:08:20 +10:00
2fd344af82 Merge branch 'master' of https://git.maxwellj.xyz/ground/ground 2025-09-20 10:35:59 +10:00
4fc76e99da Fix library issue 2025-09-20 10:35:53 +10:00
cdd1d32cee Move extlibs to ground/libraries 2025-09-19 17:55:30 +10:00
3495268672 add compilation script for linux 2025-09-19 13:09:20 +10:00
9e329968d1 Error types, print lists, better fn calling 2025-09-13 15:47:24 +10:00
e56c560514 Merge pull request 'Add Error Function' (#18) from DiamondNether90/ground_fork:master into master
Reviewed-on: max/ground#18
2025-09-12 12:05:05 +10:00
9cbe546e8a Error function 2025-09-12 11:57:45 +10:00
d9790711c6 Merge pull request 'Merge pull request 'Update docs/highlight.py' (#15) from DiamondNether90/ground_fork:master into master' (#1) from max/ground:master into master
Reviewed-on: https://git.maxwellj.xyz/DiamondNether90/ground_fork/pulls/1
2025-09-12 11:15:42 +10:00
310fede3ec Merge pull request 'Update docs/highlight.py' (#15) from DiamondNether90/ground_fork:master into master
Reviewed-on: https://git.maxwellj.xyz/max/ground/pulls/15
2025-09-02 20:42:39 +10:00
a4eba4ae47 Merge branch 'master' into master 2025-09-02 20:42:32 +10:00
e2a037befc Update docs/highlight.py
Fixed mishighlight for negative numbers
2025-09-02 08:38:54 +10:00
872392c1c5 Simple escape sequences 2025-09-01 13:10:46 +10:00
2e1e2e727b Merge pull request 'Update docs/highlight.py' (#13) from DiamondNether90/ground_fork:master into master
Reviewed-on: https://git.maxwellj.xyz/max/ground/pulls/13
2025-09-01 08:32:08 +10:00
38681f72d7 Merge branch 'master' into master 2025-09-01 08:31:46 +10:00
9c8cd58449 Update docs/highlight.py
Added file support, added inline comment support, added true/false support, fixed strings.
2025-09-01 08:30:21 +10:00
074b473bb1 Merge pull request 'Add Python Highlighting Script' (#12) from DiamondNether90/ground_fork:master into master
Reviewed-on: max/ground#12
2025-08-31 21:05:03 +10:00
a3b9cd2519 Add docs/highlight.py 2025-08-31 19:04:20 +10:00
76205a613d Rewrite label system 2025-08-31 15:04:27 +10:00
7961195018 Little thing to catch rouge functions 2025-08-30 16:28:27 +10:00
e73e5a7ebc Fix a couple small things 2025-08-30 14:20:34 +10:00
06ed44a010 Fix calling functions inside functions 2025-08-30 13:39:51 +10:00
d8cc3ff9e0 Functions can return lists 2025-08-30 13:05:28 +10:00
f32f76450a Lists are now stored in the variables map 2025-08-30 12:28:07 +10:00
cea66aa583 Only import libraries once 2025-08-30 11:12:53 +10:00
8d80416c5c Exists for lists and lines 2025-08-30 10:50:19 +10:00
0eb5670dfd Add exists instruction 2025-08-30 10:40:59 +10:00
8247ba36e4 gettype instruction 2025-08-30 10:24:31 +10:00
76e36b7ca3 Add a prefix for imported libraries 2025-08-30 10:06:25 +10:00
6596bfcc85 Organisation, documentation, mathlib 2025-08-28 11:11:59 +10:00
a9bfc1b0e3 exec external library 2025-08-25 20:17:41 +10:00
c952be1fe6 Command line arguments 2025-08-25 19:13:00 +10:00
1c5ca8d201 Simple file and request libraries 2025-08-25 18:29:45 +10:00
e56e6de911 Experimental external library support 2025-08-25 17:35:16 +10:00
38b17e7db5 Add library guide 2025-08-25 14:10:14 +10:00
e74e5ea548 Update syntax 2025-08-25 13:51:22 +10:00
f5140e3833 Delete docs/writing-a-program 2025-08-25 13:36:04 +10:00
c01dd470e1 Update docs/writing-a-program.md 2025-08-25 13:35:56 +10:00
16660c6a8d More reliable scoping 2025-08-25 13:35:22 +10:00
5bd8519517 External library support 2025-08-25 13:22:15 +10:00
2f706e2285 Update lines of code in readme 2025-08-25 11:29:43 +10:00
db99b9ac9f Fix getstrcharat error message 2025-08-25 11:22:02 +10:00
e906734aca Fix function jumping bug (I think) 2025-08-24 20:22:52 +10:00
8da5a2bf93 Experimental function jumping 2025-08-24 16:30:42 +10:00
e9600d8500 Scoping 2025-08-24 15:08:07 +10:00
1c0dfcc4b7 Fix not 2025-08-24 14:41:34 +10:00
f7f3972248 Function arguments, start of scoping 2025-08-22 13:52:26 +10:00
50d83aa228 "not" instruction 2025-08-21 11:05:32 +10:00
14758df1ab Example calculator 2025-08-21 08:43:22 +10:00
b19b4123d8 Parser string and character fix 2025-08-18 13:38:26 +10:00
28a9e389fa Basic function calling support 2025-08-18 09:36:35 +10:00
e4cc6b2f14 Fix critical bug, further functions 2025-08-15 11:35:58 +10:00
bb753e97d4 Return function 2025-08-13 18:31:54 +10:00
4cd4d9080d Function declarations 2025-08-13 09:40:03 +10:00
52eadaa9c3 Typerefs and functionrefs 2025-08-12 09:45:00 +10:00
db0c362efb Start work on functions 2025-08-11 14:57:45 +10:00
3a8600b481 Merge branch 'master' of https://git.maxwellj.xyz/max/ground 2025-08-11 14:13:00 +10:00
c39967a72f Type conversion 2025-08-11 14:12:25 +10:00
163f85b896 Fix a typo 2025-08-11 13:29:10 +10:00
09033cd432 More stuff 2025-08-11 10:07:05 +10:00
566d3aa0fb updates 2025-08-11 08:57:45 +10:00
f8397e85d4 Labels 2025-08-10 16:08:56 +10:00
3f2482d7ea Labels 2025-08-10 15:42:52 +10:00
2e388c6e68 Start work on lists 2025-08-10 13:31:28 +10:00
f43f79b869 Bugfix: no longer treats floats as ints 2025-08-09 19:52:49 +10:00
37 changed files with 3990 additions and 1054 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
ground
Bobfile
build

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "libraries"]
path = libraries
url = https://chookspace.com/ground/libraries

View File

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

22
Makefile Normal file
View 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

View File

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

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

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

70
docs/highlight.py Normal file
View File

@@ -0,0 +1,70 @@
import sys
if len(sys.argv) < 2:
exit(1)
fileName = sys.argv[1]
thefile = open(fileName).readlines()
def isnumber(num):
try:
float(num)
return True
except ValueError:
return False
allstr = ""
color = ""
keywords = ["if", "jump", "end", "stdin", "stdout", "stdlnout", "set", "gettype", "exists", "setlist", "setlistat", "getlistat", "getlistsize", "listappend", "getstrsize", "getstrcharat", "add", "subtract", "multiply", "divide", "equal", "inequal", "not", "greater", "lesser", "stoi", "stod", "tostring", "fun", "return", "endfun", "pusharg", "call", "use", "extern", "error"]
for line in thefile:
allstr += line + " <br> "
lines = len(allstr.split("<br>"))-1
a = allstr.split()
for i in range(lines):
instr = False
incom = False
words = len(allstr.split("<br>")[i].split())
for j in range(words):
tempword = allstr.split("<br>")[i].split()[j]
if allstr.split("<br>")[i].split()[0][0] == "#":
color = "\033[37m"
elif allstr.split("<br>")[i].split()[0][0] == "@":
color = "\033[32m"
elif tempword in keywords:
color = "\033[95m"
elif isnumber(tempword) or tempword == "true" or tempword == "false":
color = "\033[96m"
elif tempword[0] == "#":
incom = True
color = "\033[37m"
elif tempword[0] == "&":
color = "\033[93m"
elif tempword[0] == "$":
color = "\033[33m"
elif tempword[0] == "%":
color = "\033[32m"
elif tempword[0] == "-":
color = "\033[34m"
elif tempword[0] == "!":
color = "\033[94m"
elif tempword[0] == "*":
color = "\033[93m"
elif tempword[0] == "\"":
color = "\033[92m"
instr = not instr
elif tempword[0] == "\'" and tempword[len(tempword)-1] == "\'":
color = "\033[92m"
elif instr:
color = "\033[92m"
elif incom:
color = "\033[37m"
else:
color = "\033[91m"
print(f'{color}{tempword}', end=" ")
print()
print("\033[00m", end="")

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

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

View File

@@ -1,6 +1,6 @@
## Ground Syntax Guide
### General syntax
## General syntax
Ground uses simple instructions and arguments to run code.
@@ -32,12 +32,32 @@ Reference a line (a line reference) with a percent symbol before a line number:
jump %10
```
### Keywords
Alternatively, set a label:
```
@myLabel # The '@' symbol denotes setting a label
```
and jump to that (setting labels will be discussed below):
```
jump %myLabel
```
Add comments with a `#`:
```
# This is a comment
```
## Keywords
Note: &var can be replaced with any direct reference. $value can be replaced with a literal value or a value reference. %1 can be replaced with a line reference.
Note: In most of these functions, if a direct reference is used, the value outputted by that function will be avaliable at that variable. Any existing value inside that variable will be overwritten.
### Control Flow
#### if
Make a decision based on a boolean. If the boolean is true, jumps to the line referenced.
@@ -54,25 +74,29 @@ Usage: `jump %1`
Ends the program. Requires an integer for a status code.
Usage: `end $value`
Usage: `end $intvalue`
#### stdin
### I/O
#### 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
#### set
@@ -80,6 +104,66 @@ Allows you to set a variable to a value.
Usage: `set &var $value`
#### gettype
Gets the type of a variable. Outputs a string which can be "int", "double", "bool", "string", "char".
Usage: `gettype $value &var`
#### exists
Checks if a variable exists with a direct reference. If the variable exists, outputs true. Otherwise outputs false.
Usage `exists &var1 &var2`
Note: You can also replace &var1 with a list or line reference to check if it also exists
#### setlist
Allows you to initialize a list.
Usage: `setlist &list $value1 $value2 $value3...`
#### setlistat
Sets a list item at an index. The item at the index must already exist. Lists are index 0.
Usage: `setlistat &list $intvalue $value`
#### getlistat
Gets a list item at an index, and puts it in the variable provided. The item at the index must already exist. Lists are index 0.
Usage: `getlistat &list $intvalue &var`
#### getlistsize
Gets the size of a list and puts it in the variable provided.
Usage: `getlistsize &list &var`
#### listappend
Appends an item to a list.
Usage: `listappend &list $var`
### String Operations
#### getstrsize
Gets the size of a string and puts it in the variable provided.
Usage: `getstrsize $stringvalue &var`
#### getstrcharat
Gets a character at a certain position in a string and saves it to a variable.
Usage: `getstrcharat $stringvalue $intvalue &var`
### Maths
#### add
Adds two numbers. Numbers mean an integer or a double. Outputs to a direct reference.
@@ -104,6 +188,8 @@ Divides two numbers. Numbers mean an integer or a double. Outputs to a direct re
Usage: `divide $value $value &var`
### Comparisons
#### equal
Checks if two values are equal. Outputs a boolean to a direct reference.
@@ -116,6 +202,12 @@ Checks if two values are not equal. Outputs a boolean to a direct reference.
Usage: `inequal $value $value &var`
#### not
Negates a boolean.
Usage: `not $value &var`
#### greater
Checks if the left value is greater than the right value. Outputs a boolean to a direct reference.
@@ -126,4 +218,111 @@ Usage: `greater $value $value &var`
Checks if the left value is lesser than the right value. Outputs a boolean to a direct reference.
Usage: `lesser $value $value &var`
Usage: `lesser $value $value &var`
### Type Conversions
#### stoi
Converts a string to an integer. Throws an error if the string cannot be turned into an integer.
Usage: `stoi $stringvalue &var`
#### stod
Converts a string to a double. Throws an error if the string cannot be turned into a double.
Usage: `stod $stringvalue &var`
#### tostring
Converts any type to a string.
Usage: `tostring $value &var`
### Functions and function specific features (Experimental, please report bugs!)
Some symbols specific to this category:
* `!function`: A function reference
* `-type`: A type reference. Can be one of the following: "-string", "-char", "-int", "-double", "-bool", or a custom struct (see "Custom data structures" below)
#### fun
Defines a function. All code between `fun` and `endfun` will be included in the function.
Usage: `fun -type !functionname -type &var -type &var -type &var # and so on...`
Usage note: The first type specified before the function name must be the return type. The type displayed before all vars shows what type that variable must be.
#### return
Returns back to the main program (or other function that called this function). Also returns a value, which must be of the type defined when defining the function.
Usage: `return $value`
#### endfun
Ends a function definition. When a function reaches the end the argument list will be cleared.
Usage: `endfun`
#### pusharg
Adds a value to the argument list which will be passed to the function when it is called.
Usage: `pusharg $value`
#### call
Calls a function, with all the arguments in the argument list. The return value will be put in the specified variable.
Usage: `call !function &var
### 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
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
Attempts to import a shared object library written for Ground. All functions in the external library will be usable with `call`. All functions from the library will be given a prefix, meaning functions will be registered as `!libName:functionName`.
Note: Ground will check the directory set in the $GROUND_LIBS environment variable set by your system. The '.so' (Linux), '.dylib' (macOS), or '.dll' (Windows) extension is appended automatically.
Documentation on how to do external libraries coming soon.
Usage: `extern $stringvalue`

349
docs/tutorial.md Normal file
View 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.

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

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

1
libraries Submodule

Submodule libraries added at 5e7de482e7

244
src/data/data.cpp Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
#pragma once
#include "../main.h"
#include <string>
Literal error(std::string in, std::string errCode = "syntaxError", int exitc = 1);

File diff suppressed because it is too large Load Diff

View 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
View 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
View File

@@ -0,0 +1,6 @@
#pragma once
#include <vector>
#include <string>
std::vector<std::vector<std::string>> lexer(std::string in);

File diff suppressed because it is too large Load Diff

250
src/main.h Normal file
View 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
View 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
View 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);

12
tests/args.grnd Executable file
View File

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

23
tests/error.grnd Normal file
View 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

89
tests/everything.grnd Normal file
View File

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

17
tests/exists.grnd Normal file
View File

@@ -0,0 +1,17 @@
set &testVar "dingus"
exists &testVar &exist
stdlnout $exist
setlist &myList "item"
exists &myList &exist
stdlnout $exist
@dingus
exists %dingus &exist
stdlnout $exist
exists &doesNotExist &exist
stdlnout $exist
exists %doesNotExist &exist
stdlnout $exist

49
tests/functions.grnd Normal file
View File

@@ -0,0 +1,49 @@
fun -bool !jumpy
stdlnout "This is the jumpy function"
set &counter 0
jump %inloop
@jumpback
stdlnout "Yay I jumped!"
@inloop
add 1 $counter &counter
inequal 10 $counter &out
stdout "Condition is"
stdlnout $out
if $out %jumpback
stdlnout "Finished"
return true
endfun
!jumpy &tmp
stdlnout "I called a function"
# This function returns a list
fun -list !dingus
stdlnout "Testing lists in functions"
setlist &dingle "heheheha" "hahahahe" "hmmm"
return $dingle
endfun
call !dingus &outlist
getlistsize &outlist &size
set &counter 0
@loopstart
equal $size $counter &cond
if $cond %loopend
getlistat &outlist $counter &tmp
stdlnout $tmp
add 1 $counter &counter
jump %loopstart
@loopend
fun -int !doSomething -int &a -string &b
println $a
println $b
return 0
endfun
pusharg 432 "dingle"
!doSomething &ret

5
tests/gettype.grnd Normal file
View File

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

View File

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

14
tests/lists.grnd Normal file
View File

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

29
tests/struct.grnd Normal file
View 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

View File

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

10
tests/typeconvs.grnd Normal file
View File

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

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

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

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

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