Compare commits
7 Commits
0.0.1
...
163f85b896
Author | SHA1 | Date | |
---|---|---|---|
163f85b896 | |||
09033cd432 | |||
566d3aa0fb | |||
f8397e85d4 | |||
3f2482d7ea | |||
2e388c6e68 | |||
f43f79b869 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
ground
|
ground
|
||||||
|
Bobfile
|
||||||
|
2
Bobfile
2
Bobfile
@@ -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;
|
||||||
|
@@ -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.)
|
||||||
|
148
docs/syntax.md
148
docs/syntax.md
@@ -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 (ALL WORK IN PROGRESS)
|
||||||
|
|
||||||
|
#### 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`
|
422
src/main.cpp
422
src/main.cpp
@@ -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,278 @@ 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;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
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 +735,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 +791,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 +1322,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 +1355,13 @@ 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 error("Unexpected token: " + i);
|
else error("Unexpected token: " + i);
|
||||||
} else {
|
} else {
|
||||||
Types type = getType(i);
|
Types type = getType(i);
|
||||||
@@ -997,13 +1380,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
89
tests/everything.grnd
Normal 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
|
@@ -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
13
tests/lists.grnd
Normal 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
|
Reference in New Issue
Block a user