Compare commits

..

9 Commits

Author SHA1 Message Date
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
9 changed files with 801 additions and 22 deletions

1
.gitignore vendored
View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
## Ground Syntax Guide ## Ground Syntax Guide
### General syntax ## General syntax
Ground uses simple instructions and arguments to run code. 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 jump %10
``` ```
### Keywords Alternatively, set a label:
```
@myLabel # The '@' symbol denotes setting a label
```
and jump to that (setting labels will be discussed below):
```
jump %myLabel
```
Reference a list (a list reference) with an asterisk:
```
setlist *myList $value1 $value2 # and so on
```
## 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: &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. 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 #### if
Make a decision based on a boolean. If the boolean is true, jumps to the line referenced. Make a decision based on a boolean. If the boolean is true, jumps to the line referenced.
@@ -54,7 +74,9 @@ Usage: `jump %1`
Ends the program. Requires an integer for a status code. Ends the program. Requires an integer for a status code.
Usage: `end $value` Usage: `end $intvalue`
### I/O
#### stdin #### stdin
@@ -74,12 +96,60 @@ Allows output to the console, appending a new line at the end.
Usage: `stdlnout $value` Usage: `stdlnout $value`
### Variables and Lists
#### set #### set
Allows you to set a variable to a value. Allows you to set a variable to a value.
Usage: `set &var $value` Usage: `set &var $value`
#### 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 #### add
Adds two numbers. Numbers mean an integer or a double. Outputs to a direct reference. Adds two numbers. Numbers mean an integer or a double. Outputs to a direct reference.
@@ -104,6 +174,8 @@ Divides two numbers. Numbers mean an integer or a double. Outputs to a direct re
Usage: `divide $value $value &var` Usage: `divide $value $value &var`
### Comparisons
#### equal #### equal
Checks if two values are equal. Outputs a boolean to a direct reference. Checks if two values are equal. Outputs a boolean to a direct reference.
@@ -127,3 +199,73 @@ Usage: `greater $value $value &var`
Checks if the left value is lesser than the right value. Outputs a boolean to a direct reference. 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 (ALL WORK IN PROGRESS)
Some symbols specific to this category:
* `!function`: A function reference
* `-type`: A type reference. Can be one of the following: "-string", "-char", "-int", "-double", "-bool"
#### fun
Defines a function. All code between `fun` and `endfun` will be included in the function.
Usage: `fun !functionname -type &var -type &var -type &var # and so on...`
#### endfun
Ends a function definition. When a function reaches the end the argument list will be cleared.
Usage: `endfun`
#### pusharg
Adds a value to the argument list which will be passed to the function when it is called.
Usage: `pusharg $value`
#### call
Calls a function, with all the arguments in the argument list. The return value will be put in the specified variable.
Usage: `call !function &var
### Interacting with Libraries (ALL WORK IN PROGRESS)
#### use
Attempts to import another Ground program. Gets inserted wherever the use statement is. Any code (including code outside function declarations) will be executed.
Note: Ground will check the directory where the program is stored when trying to find imported programs. If that fails, it will check the directory set in the $GROUND_PATH environment variable set by your system. The '.grnd' extension is appended automatically.
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`.
Note: Ground will check the directory where the program is stored when trying to find external programs. If that fails, it will check the directory set in the $GROUND_PATH environment variable set by your system. The '.so', '.dll', etc extension is appended automatically.
Usage: `extern $stringvalue`

View File

@@ -20,8 +20,8 @@
With the licence out of the way, let's begin! With the licence out of the way, let's begin!
Ground is a programming language which takes some inspiration from Ground is a programming language which takes some inspiration from
Assembly in it's design, but has higher level features (more types, Assembly in its design, but has higher level features (more types,
simpler IO, easier variables, etc) which make it easy to use. simpler IO, easier variables, etc) which makes it easy to use.
Ground works even better if you write a programming language that Ground works even better if you write a programming language that
compiles to Ground code. Ground is designed to have a similar compiles to Ground code. Ground is designed to have a similar
@@ -49,7 +49,15 @@ using namespace std;
function, interpreter function, Instruction struct function, interpreter function, Instruction struct
*/ */
enum class Instructions { enum class Instructions {
Jump, Stdout, Stdin, Stdlnout, Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, If, End, Set, Empty Jump, If,
Stdout, Stdin, Stdlnout,
Add, Subtract, Multiply, Divide,
Equal, Inequal, Greater, Lesser,
End, Set, Empty,
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
Getstrcharat, Getstrsize,
Stoi, Stod, Tostring,
Fun, Endfun, Pusharg, Call
}; };
/* /*
@@ -67,7 +75,7 @@ enum class Instructions {
See also parser function See also parser function
*/ */
enum class Types { enum class Types {
Int, Double, String, Char, Bool, Value, Direct, Line Int, Double, String, Char, Bool, Value, Direct, Line, ListRef, Label
}; };
/* /*
@@ -86,12 +94,67 @@ struct Literal {
variant<int, double, bool, string, char> val; variant<int, double, bool, string, char> val;
}; };
/*
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 {
vector<Literal> val;
};
/*
ListRef struct
Contains the name of a list referenced by the program. For example, if the
following program was written:
setlist #myNums 3 5 9 13
The ListRef struct should look like this:
{
listName = "myNums";
}
*/
struct ListRef {
string listName;
};
/* /*
variables map variables map
Contains all variables made while running the program. See also Literal struct. Contains all variables made while running the program. See also Literal struct.
*/ */
map<string, Literal> variables; map<string, Literal> variables;
/*
lists map
Contains all lists made while running the program. See also List struct.
*/
map<string, List> lists;
/*
labels map
Contains all labels made in the program, for ease of jumping around the code.
*/
map<string, int> labels;
/* /*
ValueRef struct ValueRef struct
If the program being executed makes a value reference, it is stored in a ValueRef If the program being executed makes a value reference, it is stored in a ValueRef
@@ -132,6 +195,8 @@ struct Direct {
*/ */
struct Line { struct Line {
int lineNum; int lineNum;
bool isLabel = false;
string label;
}; };
/* /*
@@ -161,7 +226,7 @@ struct Line {
*/ */
struct Instruction { struct Instruction {
Instructions inst = Instructions::Empty; Instructions inst = Instructions::Empty;
vector<variant<Literal, ValueRef, Direct, Line>> args; vector<variant<Literal, ValueRef, ListRef, Direct, Line>> args;
}; };
/* /*
@@ -182,6 +247,7 @@ void error(string in, int exitc = 1) {
bool isInt(string in) { bool isInt(string in) {
try { try {
stoi(in); stoi(in);
if (stod(in) != stoi(in)) return false;
return true; return true;
} catch (...) { } catch (...) {
return false; return false;
@@ -223,7 +289,17 @@ bool isDirect(string in) {
} }
bool isLine(string in) { bool isLine(string in) {
if (in.size() >= 1 && in[0] == '%' && isInt(in.substr(1))) return true; if (in.size() >= 1 && in[0] == '%') return true;
else return false;
}
bool isLabel(string in) {
if (in.size() >= 1 && in[0] == '@') return true;
else return false;
}
bool isListRef(string in) {
if (in.size() >= 1 && in[0] == '*') return true;
else return false; else return false;
} }
@@ -241,6 +317,8 @@ Types getType(string in) {
if (isValue(in)) return Types::Value; if (isValue(in)) return Types::Value;
if (isDirect(in)) return Types::Direct; if (isDirect(in)) return Types::Direct;
if (isLine(in)) return Types::Line; if (isLine(in)) return Types::Line;
if (isLabel(in)) return Types::Label;
if (isListRef(in)) return Types::ListRef;
error("Could not determine type of \"" + in + "\""); error("Could not determine type of \"" + in + "\"");
return Types::Int; return Types::Int;
} }
@@ -254,7 +332,7 @@ Types getType(string in) {
void exec(vector<Instruction> in) { void exec(vector<Instruction> in) {
for (int i = 0; i < in.size(); i++) { for (int i = 0; i < in.size(); i++) {
Instruction l = in[i]; Instruction l = in[i];
// Pre process value references // Pre process value references and labels
for (int j = 0; j < l.args.size(); j++) { for (int j = 0; j < l.args.size(); j++) {
if (holds_alternative<ValueRef>(l.args[j])) { if (holds_alternative<ValueRef>(l.args[j])) {
if (variables.find(get<ValueRef>(l.args[j]).varName) != variables.end()) { if (variables.find(get<ValueRef>(l.args[j]).varName) != variables.end()) {
@@ -262,6 +340,17 @@ void exec(vector<Instruction> in) {
} else { } else {
error("Could not find variable " + get<ValueRef>(l.args[j]).varName); error("Could not find variable " + get<ValueRef>(l.args[j]).varName);
} }
} else if (holds_alternative<Line>(l.args[j])) {
Line ln = get<Line>(l.args[j]);
if (ln.isLabel) {
if (labels.find(ln.label) != labels.end()) {
Line newLine;
newLine.lineNum = labels[ln.label];
l.args[j] = newLine;
} else {
error("Could not find label " + ln.label);
}
}
} }
} }
switch (l.inst) { switch (l.inst) {
@@ -367,6 +456,399 @@ void exec(vector<Instruction> in) {
variables[varName] = varContents; variables[varName] = varContents;
} }
break; break;
/*
setlist instruction
This instruction takes a potentially infinite amount of arguments and
saves them to a list (vector) with name listName.
*/
case Instructions::Setlist:
if (l.args.size() < 2) {
error("Could not find all arguments required for Setlist inbuilt");
}
{
string listName;
List listContents;
if (holds_alternative<ListRef>(l.args[0])) {
listName = get<ListRef>(l.args[0]).listName;
} else {
error("First argument of setlist must be a list reference");
}
bool first = true;
for (variant<Literal, ValueRef, ListRef, Direct, Line> k : l.args) {
if (holds_alternative<Literal>(k)) {
listContents.val.push_back(get<Literal>(k));
} else {
if (!first) error("All arguments after first in setlist must be values");
first = false;
}
}
lists[listName] = listContents;
}
break;
/*
getlistat instruction
This instruction gets a list item from a list and an index and saves the
value to a variable.
*/
case Instructions::Getlistat:
if (l.args.size() < 3) {
error("Could not find all arguments required for Getlistat inbuilt");
}
{
ListRef listref;
int ref;
Direct var;
if (holds_alternative<ListRef>(l.args[0])) {
listref = get<ListRef>(l.args[0]);
} else {
error("First argument of getlistat must be a list reference");
}
if (holds_alternative<Literal>(l.args[1])) {
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
ref = get<int>(get<Literal>(l.args[1]).val);
} else {
error("Second argument of getlistat must be an integer literal");
}
} else {
error("Second argument of getlistat must be an integer literal");
}
if (holds_alternative<Direct>(l.args[2])) {
var = get<Direct>(l.args[2]);
} else {
error("Third argument of getlistat must be a direct reference");
}
if (lists.find(listref.listName) != lists.end()) {
if (lists[listref.listName].val.size() > ref) {
variables[var.varName] = lists[listref.listName].val[ref];
} else {
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
}
} else {
error("Unknown list: " + listref.listName);
}
}
break;
/*
getstrcharat instruction
This instruction gets a character from a string at an index and saves it
to a variable.
*/
case Instructions::Getstrcharat:
if (l.args.size() < 3) {
error("Could not find all arguments required for Getstrcharat inbuilt");
}
{
string instr;
int ref;
Direct var;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
instr = get<string>(get<Literal>(l.args[0]).val);
} else {
error("First argument of getlistat must be a string literal");
}
} else {
error("First argument of getlistat must be a string literal");
}
if (holds_alternative<Literal>(l.args[1])) {
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
ref = get<int>(get<Literal>(l.args[1]).val);
} else {
error("Second argument of getlistat must be an integer literal");
}
} else {
error("Second argument of getlistat must be an integer literal");
}
if (holds_alternative<Direct>(l.args[2])) {
var = get<Direct>(l.args[2]);
} else {
error("Third argument of getlistat must be a direct reference");
}
if (instr.size() > ref) {
Literal newLit;
newLit.val = instr[ref];
variables[var.varName] = newLit;
} else {
error("Index " + to_string(ref) + " out of range of string " + instr);
}
}
break;
/*
setlistat instruction
This instruction sets an item in a list to be a certain value.
*/
case Instructions::Setlistat:
if (l.args.size() < 3) {
error("Could not find all arguments required for Setlistat inbuilt");
}
{
ListRef listref;
int ref;
Literal value;
if (holds_alternative<ListRef>(l.args[0])) {
listref = get<ListRef>(l.args[0]);
} else {
error("First argument of setlistat must be a list reference");
}
if (holds_alternative<Literal>(l.args[1])) {
if (holds_alternative<int>(get<Literal>(l.args[1]).val)) {
ref = get<int>(get<Literal>(l.args[1]).val);
} else {
error("Second argument of setlistat must be an integer literal");
}
} else {
error("Second argument of setlistat must be an integer literal");
}
if (holds_alternative<Literal>(l.args[2])) {
value = get<Literal>(l.args[2]);
} else {
error("Third argument of setlistat must be a direct reference");
}
if (lists.find(listref.listName) != lists.end()) {
if (lists[listref.listName].val.size() > ref) {
lists[listref.listName].val[ref] = value;
} else {
error("Index " + to_string(ref) + " out of range of list " + listref.listName);
}
} else {
error("Unknown list: " + listref.listName);
}
}
break;
/*
listappend instruction
This instruction appends an item to a list.
*/
case Instructions::Listappend:
if (l.args.size() < 2) {
error("Could not find all arguments required for Listappend inbuilt");
}
{
ListRef listref;
Literal value;
if (holds_alternative<ListRef>(l.args[0])) {
listref = get<ListRef>(l.args[0]);
} else {
error("Second argument of listappend must be a list reference");
}
if (holds_alternative<Literal>(l.args[1])) {
value = get<Literal>(l.args[1]);
} else {
error("Second argument of listappend must be a direct reference");
}
if (lists.find(listref.listName) != lists.end()) {
lists[listref.listName].val.push_back(value);
} else {
error("Unknown list: " + listref.listName);
}
}
break;
/*
getlistsize instruction
This instruction saves the size of a list in a variable.
*/
case Instructions::Getlistsize:
if (l.args.size() < 2) {
error("Could not find all arguments required for Getlistsize inbuilt");
}
{
ListRef ref;
Direct var;
if (holds_alternative<ListRef>(l.args[0])) {
ref = get<ListRef>(l.args[0]);
} else {
error("First argument of getlistsize must be a list reference");
}
if (holds_alternative<Direct>(l.args[1])) {
var = get<Direct>(l.args[1]);
} else {
error("Second argument of getlistsize must be a direct reference");
}
Literal newLit;
if (lists.find(ref.listName) != lists.end()) {
newLit.val = int(lists[ref.listName].val.size());
variables[var.varName] = newLit;
} else {
error("Couldn't find the list " + ref.listName);
}
break;
}
/*
getstrsize instruction
This instruction saves the size of a string in a variable.
*/
case Instructions::Getstrsize:
if (l.args.size() < 2) {
error("Could not find all arguments required for Getstrsize inbuilt");
}
{
string ref;
Direct var;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
ref = get<string>(get<Literal>(l.args[0]).val);
} else {
error("First argument of getlistsize must be a string value");
}
} else {
error("First argument of getlistsize must be a string value");
}
if (holds_alternative<Direct>(l.args[1])) {
var = get<Direct>(l.args[1]);
} else {
error("Second argument of getlistsize must be a direct reference");
}
Literal newLit;
newLit.val = int(ref.size());
variables[var.varName] = newLit;
break;
}
/*
stoi instruction
This function converts a string to an int, and saves it in a variable.
If the string cannot be turned into an integer, there is an error.
*/
case Instructions::Stoi:
if (l.args.size() < 2) {
error("Could not find all arguments required for Stoi inbuilt");
}
{
string toConv;
Direct ref;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
toConv = get<string>(get<Literal>(l.args[0]).val);
} else {
error("First argument of stoi must be a string literal");
}
} else {
error("First argument of stoi must be a string literal");
}
if (holds_alternative<Direct>(l.args[1])) {
ref = get<Direct>(l.args[1]);
} else {
error("Second argument of stoi must be a direct reference");
}
if (isInt(toConv)) {
Literal newLit;
newLit.val = stoi(toConv);
variables[ref.varName] = newLit;
} else {
error("Cannot convert the value " + toConv + " to an int");
}
}
break;
/*
stod instruction
This function converts a string to a decimal, and saves it in a variable.
If the string cannot be turned into a decimal, there is an error.
*/
case Instructions::Stod:
if (l.args.size() < 2) {
error("Could not find all arguments required for Stod inbuilt");
}
{
string toConv;
Direct ref;
if (holds_alternative<Literal>(l.args[0])) {
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
toConv = get<string>(get<Literal>(l.args[0]).val);
} else {
error("First argument of stod must be a string literal");
}
} else {
error("First argument of stod must be a string literal");
}
if (holds_alternative<Direct>(l.args[1])) {
ref = get<Direct>(l.args[1]);
} else {
error("Second argument of stod must be a direct reference");
}
if (isDouble(toConv) || isInt(toConv)) {
Literal newLit;
newLit.val = stod(toConv);
variables[ref.varName] = newLit;
} else {
error("Cannot convert the value " + toConv + " to a decimal");
}
}
break;
/*
tostring instruction
This function converts any type to a string, and saves it in a variable.
*/
case Instructions::Tostring:
if (l.args.size() < 2) {
error("Could not find all arguments required for Tostring inbuilt");
}
{
Literal toConv;
Direct ref;
if (holds_alternative<Literal>(l.args[0])) {
toConv = get<Literal>(l.args[0]);
} else {
error("First argument of tostring must be a literal");
}
if (holds_alternative<Direct>(l.args[1])) {
ref = get<Direct>(l.args[1]);
} else {
error("Second argument of tostring must be a direct reference");
}
Literal newLit;
if (holds_alternative<int>(toConv.val)) {
newLit.val = to_string(get<int>(toConv.val));
} else if (holds_alternative<double>(toConv.val)) {
newLit.val = to_string(get<double>(toConv.val));
} else if (holds_alternative<string>(toConv.val)) {
newLit.val = get<string>(toConv.val);
} else if (holds_alternative<char>(toConv.val)) {
newLit.val = string().append(&get<char>(toConv.val));
} else if (holds_alternative<bool>(toConv.val)) {
if (get<bool>(toConv.val)) {
newLit.val = "true";
} else {
newLit.val = "false";
}
}
variables[ref.varName] = newLit;
}
break;
/* /*
stdin instruction stdin instruction
This instruction takes input from the standard character input via This instruction takes input from the standard character input via
@@ -374,7 +856,7 @@ void exec(vector<Instruction> in) {
*/ */
case Instructions::Stdin: case Instructions::Stdin:
if (l.args.size() < 1) { if (l.args.size() < 1) {
error("Could not find all arguments require for Stdin inbuilt"); error("Could not find all arguments required for Stdin inbuilt");
} }
if (holds_alternative<Direct>(l.args[0])) { if (holds_alternative<Direct>(l.args[0])) {
string userIn; string userIn;
@@ -430,6 +912,12 @@ void exec(vector<Instruction> in) {
final.val = get<double>(left.val) + double(get<int>(right.val)); final.val = get<double>(left.val) + double(get<int>(right.val));
} else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) { } else if (holds_alternative<string>(left.val) && holds_alternative<string>(right.val)) {
final.val = get<string>(left.val) + get<string>(right.val); final.val = get<string>(left.val) + get<string>(right.val);
} else if (holds_alternative<string>(left.val) && holds_alternative<char>(right.val)) {
final.val = get<string>(left.val).append(&get<char>(right.val));
} else if (holds_alternative<char>(left.val) && holds_alternative<string>(right.val)) {
final.val = string().append(&get<char>(left.val)) + get<string>(right.val);
} else if (holds_alternative<char>(left.val) && holds_alternative<char>(right.val)) {
final.val = string().append(&get<char>(left.val)).append(&get<char>(right.val));
} else { } else {
error("Cannot add those two values"); error("Cannot add those two values");
} }
@@ -955,16 +1443,25 @@ vector<vector<string>> lexer(string in) {
vector<Instruction> parser(vector<vector<string>> in) { vector<Instruction> parser(vector<vector<string>> in) {
vector<Instruction> out; vector<Instruction> out;
int lineNum = 1;
for (vector<string> lineTokens : in) { for (vector<string> lineTokens : in) {
if (lineTokens.empty()) continue; lineNum ++;
Instruction newInst; Instruction newInst;
if (lineTokens.empty()) {
out.push_back(newInst);
continue;
};
bool firstInst = true; bool firstInst = true;
for (string i : lineTokens) { for (string i : lineTokens) {
if (firstInst) { if (firstInst) {
firstInst = false; firstInst = false;
if (i == "stdin") newInst.inst = Instructions::Stdin; if (isLabel(i)) {
labels[i.substr(1)] = lineNum;
out.push_back(newInst);
continue;
}
else if (i == "stdin") newInst.inst = Instructions::Stdin;
else if (i == "stdout") newInst.inst = Instructions::Stdout; else if (i == "stdout") newInst.inst = Instructions::Stdout;
else if (i == "stdlnout") newInst.inst = Instructions::Stdlnout; else if (i == "stdlnout") newInst.inst = Instructions::Stdlnout;
else if (i == "jump") newInst.inst = Instructions::Jump; else if (i == "jump") newInst.inst = Instructions::Jump;
@@ -979,6 +1476,16 @@ vector<Instruction> parser(vector<vector<string>> in) {
else if (i == "inequal") newInst.inst = Instructions::Inequal; else if (i == "inequal") newInst.inst = Instructions::Inequal;
else if (i == "end") newInst.inst = Instructions::End; else if (i == "end") newInst.inst = Instructions::End;
else if (i == "set") newInst.inst = Instructions::Set; else if (i == "set") newInst.inst = Instructions::Set;
else if (i == "setlist") newInst.inst = Instructions::Setlist;
else if (i == "setlistat") newInst.inst = Instructions::Setlistat;
else if (i == "getlistat") newInst.inst = Instructions::Getlistat;
else if (i == "getlistsize") newInst.inst = Instructions::Getlistsize;
else if (i == "listappend") newInst.inst = Instructions::Listappend;
else if (i == "getstrsize") newInst.inst = Instructions::Getstrsize;
else if (i == "getstrcharat") newInst.inst = Instructions::Getstrcharat;
else if (i == "stoi") newInst.inst = Instructions::Stoi;
else if (i == "stod") newInst.inst = Instructions::Stod;
else if (i == "tostring") newInst.inst = Instructions::Tostring;
else error("Unexpected token: " + i); else error("Unexpected token: " + i);
} else { } else {
Types type = getType(i); Types type = getType(i);
@@ -997,13 +1504,28 @@ vector<Instruction> parser(vector<vector<string>> in) {
newInst.args.push_back(newDirect); newInst.args.push_back(newDirect);
} }
break; break;
case Types::Label:
error("Label should be defined as first token");
break;
case Types::Line: case Types::Line:
{ {
Line newLine; Line newLine;
newLine.lineNum = stoi(i.substr(1)); if (isInt(i.substr(1))) newLine.lineNum = stoi(i.substr(1));
else {
newLine.isLabel = true;
newLine.label = i.substr(1);
newLine.lineNum = 0;
}
newInst.args.push_back(newLine); newInst.args.push_back(newLine);
} }
break; break;
case Types::ListRef:
{
ListRef newLR;
newLR.listName = i.substr(1);
newInst.args.push_back(newLR);
}
break;
case Types::String: case Types::String:
{ {
Literal newLiteral; Literal newLiteral;

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

View File

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

13
tests/lists.grnd Normal file
View File

@@ -0,0 +1,13 @@
# A cool list
setlist *favWords "hello" "there" "general" "kenobi"
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

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