Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7bf18a744c | |||
| fa20763c07 | |||
| 774f3d9e83 | |||
| 2d18ff4ba3 | |||
| 30b068fcdb | |||
| 3d8ce4e7d8 | |||
| e0ab4106fa | |||
| 2c143a8be5 | |||
| 0aa6e4063b | |||
| ec39f099ca | |||
| 4dcc05e796 | |||
| 2af95ec82e | |||
| bbdb521333 | |||
| 56987bc96a | |||
| 8498864dde | |||
| 14710dec90 | |||
| d14177dbca | |||
| 42580bbe2a | |||
| 0c8e036de8 | |||
| a55338d8e3 | |||
| 5d240f85a6 | |||
| cceefa6375 | |||
| 632ed7c089 | |||
| c1528f5501 | |||
| 6c5d952c44 | |||
| 208ad9d615 | |||
| 876aad853c | |||
| 1baa65cc95 |
@@ -0,0 +1,19 @@
|
|||||||
|
name: Comprehensive Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: install valgrind
|
||||||
|
run: sudo apt install valgrind
|
||||||
|
- name: make test
|
||||||
|
run: make test
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<image src="toylogo.png" />
|
<image src="toylogo.png" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Toy 0.6.0
|
# Toy
|
||||||
|
|
||||||
This is the Toy programming language interpreter, written in C.
|
This is the Toy programming language interpreter, written in C.
|
||||||
|
|
||||||
@@ -27,6 +27,12 @@ Note: For Linux, you may need to `cd` into the `out` directory before running.
|
|||||||
|
|
||||||
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
|
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
Run `make install-tools` to install a number of tools, including:
|
||||||
|
|
||||||
|
* VSCode syntax highlighting
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ repl-static: $(TOY_OUTDIR) static
|
|||||||
$(MAKE) -C repl
|
$(MAKE) -C repl
|
||||||
|
|
||||||
library: $(TOY_OUTDIR)
|
library: $(TOY_OUTDIR)
|
||||||
$(MAKE) -C source library
|
$(MAKE) -j8 -C source library
|
||||||
|
|
||||||
static: $(TOY_OUTDIR)
|
static: $(TOY_OUTDIR)
|
||||||
$(MAKE) -C source static
|
$(MAKE) -j8 -C source static
|
||||||
|
|
||||||
test: clean $(TOY_OUTDIR)
|
test: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -C test
|
$(MAKE) -C test
|
||||||
@@ -23,6 +23,10 @@ test: clean $(TOY_OUTDIR)
|
|||||||
$(TOY_OUTDIR):
|
$(TOY_OUTDIR):
|
||||||
mkdir $(TOY_OUTDIR)
|
mkdir $(TOY_OUTDIR)
|
||||||
|
|
||||||
|
#utils
|
||||||
|
install-tools:
|
||||||
|
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
|
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
//no arguments
|
//no arguments
|
||||||
|
|||||||
@@ -0,0 +1,411 @@
|
|||||||
|
#include "lib_timer.h"
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
|
||||||
|
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
|
||||||
|
//normallize
|
||||||
|
if (x->tv_usec > 999999) {
|
||||||
|
x->tv_sec += x->tv_usec / 1000000;
|
||||||
|
x->tv_usec %= 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y->tv_usec > 999999) {
|
||||||
|
y->tv_sec += y->tv_usec / 1000000;
|
||||||
|
y->tv_usec %= 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
//calc
|
||||||
|
result->tv_sec = x->tv_sec - y->tv_sec;
|
||||||
|
|
||||||
|
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
|
||||||
|
if (result->tv_sec != 0) { //only works far from 0
|
||||||
|
result->tv_usec += 1000000;
|
||||||
|
result->tv_sec--; // borrow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//god damn it
|
||||||
|
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
|
||||||
|
struct timeval* d = ALLOCATE(struct timeval, 1);
|
||||||
|
|
||||||
|
//I gave up, copied from SO
|
||||||
|
timeval_subtract(d, rhs, lhs);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
//callbacks
|
||||||
|
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 0) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the timeinfo from C
|
||||||
|
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
||||||
|
gettimeofday(timeinfo, NULL);
|
||||||
|
|
||||||
|
//wrap in an opaque literal for Toy
|
||||||
|
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||||
|
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the timeinfo from C
|
||||||
|
struct timeval timerStop;
|
||||||
|
gettimeofday(&timerStop, NULL);
|
||||||
|
|
||||||
|
//unwrap the opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//determine the difference, and wrap it
|
||||||
|
struct timeval* d = diff(timerStart, &timerStop);
|
||||||
|
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(diffLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 2) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the args
|
||||||
|
Literal microsecondLiteral = popLiteralArray(arguments);
|
||||||
|
Literal secondLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal secondLiteralIdn = secondLiteral;
|
||||||
|
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
|
||||||
|
freeLiteral(secondLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal microsecondLiteralIdn = microsecondLiteral;
|
||||||
|
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, µsecondLiteral)) {
|
||||||
|
freeLiteral(microsecondLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
|
||||||
|
freeLiteral(secondLiteral);
|
||||||
|
freeLiteral(microsecondLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) {
|
||||||
|
interpreter->errorOutput("Microseconds out of range in createTimer\n");
|
||||||
|
freeLiteral(secondLiteral);
|
||||||
|
freeLiteral(microsecondLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the timeinfo from toy
|
||||||
|
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
||||||
|
timeinfo->tv_sec = AS_INTEGER(secondLiteral);
|
||||||
|
timeinfo->tv_usec = AS_INTEGER(microsecondLiteral);
|
||||||
|
|
||||||
|
//wrap in an opaque literal for Toy
|
||||||
|
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||||
|
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(secondLiteral);
|
||||||
|
freeLiteral(microsecondLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap the opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//create the result literal
|
||||||
|
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
|
||||||
|
pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(result);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap the opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//create the result literal
|
||||||
|
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
|
||||||
|
pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(result);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 2) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap the opaque literals
|
||||||
|
Literal rhsLiteral = popLiteralArray(arguments);
|
||||||
|
Literal lhsLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal lhsLiteralIdn = lhsLiteral;
|
||||||
|
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
|
||||||
|
freeLiteral(lhsLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal rhsLiteralIdn = rhsLiteral;
|
||||||
|
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
|
||||||
|
freeLiteral(rhsLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
|
||||||
|
freeLiteral(lhsLiteral);
|
||||||
|
freeLiteral(rhsLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
|
||||||
|
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
|
||||||
|
|
||||||
|
//determine the difference, and wrap it
|
||||||
|
struct timeval* d = diff(lhsTimer, rhsTimer);
|
||||||
|
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(lhsLiteral);
|
||||||
|
freeLiteral(rhsLiteral);
|
||||||
|
freeLiteral(diffLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap in an opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//create the string literal
|
||||||
|
Literal resultLiteral = TO_NULL_LITERAL;
|
||||||
|
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
|
||||||
|
char buffer[128];
|
||||||
|
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
|
||||||
|
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
|
||||||
|
}
|
||||||
|
else { //normal case
|
||||||
|
char buffer[128];
|
||||||
|
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
|
||||||
|
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(resultLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap in an opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
FREE(struct timeval, timer);
|
||||||
|
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
char* name;
|
||||||
|
NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"startTimer", nativeStartTimer},
|
||||||
|
{"_stopTimer", nativeStopTimer},
|
||||||
|
{"createTimer", nativeCreateTimer},
|
||||||
|
{"_getTimerSeconds", nativeGetTimerSeconds},
|
||||||
|
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
|
||||||
|
{"_compareTimer", nativeCompareTimer},
|
||||||
|
{"_timerToString", nativeTimerToString},
|
||||||
|
{"_destroyTimer", nativeDestroyTimer},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
freeLiteral(alias);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
|
||||||
|
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||||
|
func.type = LITERAL_FUNCTION_NATIVE;
|
||||||
|
|
||||||
|
setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
freeLiteral(name);
|
||||||
|
freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
||||||
|
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
||||||
|
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(dict);
|
||||||
|
freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
|
#include "lib_timer.h"
|
||||||
|
|
||||||
#include "console_colors.h"
|
#include "console_colors.h"
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ void repl() {
|
|||||||
|
|
||||||
//inject the libs
|
//inject the libs
|
||||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||||
|
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
printf("> ");
|
printf("> ");
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
|
#include "lib_timer.h"
|
||||||
|
|
||||||
#include "console_colors.h"
|
#include "console_colors.h"
|
||||||
|
|
||||||
@@ -108,6 +109,7 @@ void runBinary(unsigned char* tb, size_t size) {
|
|||||||
|
|
||||||
//inject the libs
|
//inject the libs
|
||||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||||
|
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||||
|
|
||||||
runInterpreter(&interpreter, tb, size);
|
runInterpreter(&interpreter, tb, size);
|
||||||
freeInterpreter(&interpreter);
|
freeInterpreter(&interpreter);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
char* readFile(char* path, size_t* fileSize);
|
char* readFile(char* path, size_t* fileSize);
|
||||||
void writeFile(char* path, unsigned char* bytes, size_t size);
|
void writeFile(char* path, unsigned char* bytes, size_t size);
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ fn fib(n : int) {
|
|||||||
return fib(n-1) + fib(n-2);
|
return fib(n-1) + fib(n-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 40; i++) {
|
for (var i = 0; i < 20; i++) {
|
||||||
var res = fib(i);
|
var res = fib(i);
|
||||||
print (string)i + ": " + (string)res;
|
print string i + ": " + string res;
|
||||||
}
|
}
|
||||||
+9
-52
@@ -1,56 +1,13 @@
|
|||||||
|
import timer;
|
||||||
|
|
||||||
|
var a = createTimer(1, 0);
|
||||||
|
var b = createTimer(2, 0);
|
||||||
|
|
||||||
fn _b(self) {
|
print a.compareTimer(b).timerToString();
|
||||||
print "running _b";
|
print b.compareTimer(a).timerToString();
|
||||||
print self;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _c(self) {
|
var c = createTimer(0, 1);
|
||||||
print "running _c";
|
var d = createTimer(0, 2);
|
||||||
print self;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _d(self) {
|
print c.compareTimer(d).timerToString();
|
||||||
print "running _d";
|
print d.compareTimer(c).timerToString();
|
||||||
print self;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _e(self) {
|
|
||||||
print "running _e";
|
|
||||||
print self;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _f(self) {
|
|
||||||
print "running _f";
|
|
||||||
print self;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn b() {
|
|
||||||
print "running b";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn c() {
|
|
||||||
print "running c";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn d() {
|
|
||||||
print "running d";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn e() {
|
|
||||||
print "running e";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn f() {
|
|
||||||
print "running f";
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = 42;
|
|
||||||
|
|
||||||
print a.b().c().d().e().f();
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
fn panic() {
|
|
||||||
assert false, "This should only be seen once";
|
|
||||||
}
|
|
||||||
|
|
||||||
panic();
|
|
||||||
panic();
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
#include "literal.h"
|
#include "literal.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include "token_types.h"
|
#include "token_types.h"
|
||||||
|
|||||||
+1
-1
@@ -891,7 +891,7 @@ int _index(Interpreter* interpreter, LiteralArray* arguments) {
|
|||||||
//else override elements of the array instead
|
//else override elements of the array instead
|
||||||
else {
|
else {
|
||||||
//copy compound to result
|
//copy compound to result
|
||||||
snprintf(result, MAX_STRING_LENGTH, AS_STRING(compound));
|
snprintf(result, MAX_STRING_LENGTH, "%s", AS_STRING(compound));
|
||||||
|
|
||||||
int assignLength = strlen(AS_STRING(assign));
|
int assignLength = strlen(AS_STRING(assign));
|
||||||
int min = AS_INTEGER(third) > 0 ? AS_INTEGER(first) : AS_INTEGER(second) - 1;
|
int min = AS_INTEGER(third) > 0 ? AS_INTEGER(first) : AS_INTEGER(second) - 1;
|
||||||
|
|||||||
+1
-1
@@ -333,7 +333,7 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, ASTNode* node, void* br
|
|||||||
return node->binary.opcode;
|
return node->binary.opcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != OP_EOF && (node->binary.opcode == OP_AND || node->binary.opcode == OP_OR || (node->binary.opcode >= OP_COMPARE_EQUAL && node->binary.opcode <= OP_INVERT))) {
|
if (ret != OP_EOF && (node->binary.opcode == OP_VAR_ASSIGN || node->binary.opcode == OP_AND || node->binary.opcode == OP_OR || (node->binary.opcode >= OP_COMPARE_EQUAL && node->binary.opcode <= OP_INVERT))) {
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
|
||||||
ret = OP_EOF; //untangle in this case
|
ret = OP_EOF; //untangle in this case
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include "ast_node.h"
|
#include "ast_node.h"
|
||||||
#include "literal_array.h"
|
#include "literal_array.h"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "interpreter.h"
|
#include "interpreter.h"
|
||||||
#include "console_colors.h"
|
#include "console_colors.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "keyword_types.h"
|
#include "keyword_types.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
#include "literal.h"
|
#include "literal.h"
|
||||||
#include "literal_array.h"
|
#include "literal_array.h"
|
||||||
#include "literal_dictionary.h"
|
#include "literal_dictionary.h"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "keyword_types.h"
|
#include "keyword_types.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
#include "token_types.h"
|
#include "token_types.h"
|
||||||
|
|
||||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ static unsigned int hashString(const char* string, int length) {
|
|||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
hash *= string[i];
|
hash *= string[i];
|
||||||
hash *= 16777619;
|
hash ^= 16777619;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include "literal.h"
|
#include "literal.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include "literal.h"
|
#include "literal.h"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
|
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
|
||||||
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
|
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
|
||||||
|
|||||||
+1
-8
@@ -1,7 +1,5 @@
|
|||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "literal.h"
|
#include "literal.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
@@ -798,12 +796,6 @@ static Opcode dot(Parser* parser, ASTNode** nodeHandle) {
|
|||||||
return OP_EOF;
|
return OP_EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
//hijack the function call, and hack in an extra parameter
|
|
||||||
if (node->binary.opcode == OP_FN_CALL) {
|
|
||||||
node->binary.right->fnCall.argumentCount++;
|
|
||||||
node->binary.opcode = OP_DOT;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*nodeHandle) = node;
|
(*nodeHandle) = node;
|
||||||
return OP_DOT; //signal that the function name and arguments are in the wrong order
|
return OP_DOT; //signal that the function name and arguments are in the wrong order
|
||||||
}
|
}
|
||||||
@@ -1090,6 +1082,7 @@ static void dottify(Parser* parser, ASTNode** nodeHandle) {
|
|||||||
if ((*nodeHandle)->type == AST_NODEBINARY) {
|
if ((*nodeHandle)->type == AST_NODEBINARY) {
|
||||||
if ((*nodeHandle)->binary.opcode == OP_FN_CALL) {
|
if ((*nodeHandle)->binary.opcode == OP_FN_CALL) {
|
||||||
(*nodeHandle)->binary.opcode = OP_DOT;
|
(*nodeHandle)->binary.opcode = OP_DOT;
|
||||||
|
(*nodeHandle)->binary.right->fnCall.argumentCount++;
|
||||||
}
|
}
|
||||||
dottify(parser, &(*nodeHandle)->binary.left);
|
dottify(parser, &(*nodeHandle)->binary.left);
|
||||||
dottify(parser, &(*nodeHandle)->binary.right);
|
dottify(parser, &(*nodeHandle)->binary.right);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "ast_node.h"
|
#include "ast_node.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -6,14 +6,25 @@
|
|||||||
|
|
||||||
#define TOY_VERSION_MAJOR 0
|
#define TOY_VERSION_MAJOR 0
|
||||||
#define TOY_VERSION_MINOR 6
|
#define TOY_VERSION_MINOR 6
|
||||||
#define TOY_VERSION_PATCH 0
|
#define TOY_VERSION_PATCH 2
|
||||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||||
|
|
||||||
//platform exports/imports
|
//platform exports/imports
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#define TOY_API extern
|
#define TOY_API extern
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#elif defined(_WIN32) || defined(WIN32)
|
||||||
|
#define TOY_API
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define TOY_API
|
#define TOY_API
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
#ifndef TOY_EXPORT
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
NOTES: For some reason, this code results in the error:
|
||||||
|
Undeclared variable "inner"
|
||||||
|
|
||||||
|
It only occurs under these very specific conditions.
|
||||||
|
|
||||||
|
It appears to be a compiler issue, see issue #38 for more info.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn _getValue(self) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cache;
|
||||||
|
cache = 42.getValue(); //assignment, rather than declaration, allows the bug
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
fn _add(self, inc) {
|
||||||
|
return self + inc;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert 1.add(2).add(3).add(4) == 10, "dottify bugfix failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
//test the timer library
|
||||||
|
{
|
||||||
|
//create a timer, run it for a short period
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
var timerA: opaque = startTimer();
|
||||||
|
for (var i: int = 0; i < 1000 * 1; i++);
|
||||||
|
var diffA: opaque = timerA.stopTimer();
|
||||||
|
|
||||||
|
//create another timer, run it for a longer period
|
||||||
|
var timerB: opaque = startTimer();
|
||||||
|
for (var i: int = 0; i < 1000 * 10; i++);
|
||||||
|
var diffB: opaque = timerB.stopTimer();
|
||||||
|
|
||||||
|
//check to ensure that the second timer took longer than the first
|
||||||
|
//WARNING: this has the small possibility of failing due to external factors
|
||||||
|
var difference: opaque = diffA.compareTimer(diffB);
|
||||||
|
|
||||||
|
assert difference.getTimerSeconds() >= 0, "compareTimer() (seconds) failed";
|
||||||
|
if (difference.getTimerSeconds() == 0) {
|
||||||
|
assert difference.getTimerMicroseconds() >= 0, "compareTimer() (microseconds) failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
//all timers must be destroyed after use
|
||||||
|
timerA.destroyTimer();
|
||||||
|
timerB.destroyTimer();
|
||||||
|
diffA.destroyTimer();
|
||||||
|
diffB.destroyTimer();
|
||||||
|
difference.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//create a timer, manipulate it's values
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
//set the timer values manually
|
||||||
|
var timer: opaque = createTimer(42, 8891);
|
||||||
|
|
||||||
|
//check the timer values
|
||||||
|
assert timer.getTimerSeconds() == 42, "getTimerSeconds() failed";
|
||||||
|
assert timer.getTimerMicroseconds() == 8891, "getTimerMicroseconds() failed";
|
||||||
|
|
||||||
|
//all timers must be destroyed after use
|
||||||
|
timer.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//set a timer to check string representation
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
var timer: opaque = createTimer(42, 999);
|
||||||
|
|
||||||
|
assert timer.timerToString() == "42.000999", "timerToString() failed";
|
||||||
|
|
||||||
|
//all timers must be destroyed after use
|
||||||
|
timer.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//test positive and negative values of timers
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
var a = createTimer(1, 0);
|
||||||
|
var b = createTimer(2, 0);
|
||||||
|
|
||||||
|
var acmp = a.compareTimer(b);
|
||||||
|
var bcmp = b.compareTimer(a);
|
||||||
|
|
||||||
|
var c = createTimer(0, 1);
|
||||||
|
var d = createTimer(0, 2);
|
||||||
|
|
||||||
|
var ccmp = c.compareTimer(d);
|
||||||
|
var dcmp = d.compareTimer(c);
|
||||||
|
|
||||||
|
assert acmp.timerToString() == "1.000000", "positive and negative tests failed (acmp)";
|
||||||
|
assert bcmp.timerToString() == "-1.000000", "positive and negative tests failed (bcmp)";
|
||||||
|
assert ccmp.timerToString() == "0.000001", "positive and negative tests failed (ccmp)";
|
||||||
|
assert dcmp.timerToString() == "-0.000001", "positive and negative tests failed (dcmp)";
|
||||||
|
|
||||||
|
a.destroyTimer();
|
||||||
|
b.destroyTimer();
|
||||||
|
c.destroyTimer();
|
||||||
|
d.destroyTimer();
|
||||||
|
acmp.destroyTimer();
|
||||||
|
bcmp.destroyTimer();
|
||||||
|
ccmp.destroyTimer();
|
||||||
|
dcmp.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
fn panic() {
|
||||||
|
assert false, "!ignore panicking within a function";
|
||||||
|
}
|
||||||
|
|
||||||
|
panic();
|
||||||
|
panic();
|
||||||
@@ -90,14 +90,14 @@ unsigned char* compileString(char* source, size_t* size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void error(char* msg) {
|
void error(char* msg) {
|
||||||
printf(msg);
|
printf("%s", msg);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char* source = readFile("../scripts//test/call-from-host.toy", &size);
|
char* source = readFile("scripts/call-from-host.toy", &size);
|
||||||
unsigned char* tb = compileString(source, &size);
|
unsigned char* tb = compileString(source, &size);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
|
|
||||||
@@ -281,6 +281,8 @@ int main() {
|
|||||||
{
|
{
|
||||||
interpreter.printOutput("Testing assertion failure");
|
interpreter.printOutput("Testing assertion failure");
|
||||||
|
|
||||||
|
setInterpreterAssert(&interpreter, noPrintFn);
|
||||||
|
|
||||||
LiteralArray arguments;
|
LiteralArray arguments;
|
||||||
initLiteralArray(&arguments);
|
initLiteralArray(&arguments);
|
||||||
LiteralArray returns;
|
LiteralArray returns;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ int main() {
|
|||||||
{
|
{
|
||||||
//source
|
//source
|
||||||
size_t sourceLength = 0;
|
size_t sourceLength = 0;
|
||||||
char* source = readFile("sample_code.toy", &sourceLength);
|
char* source = readFile("scripts/sample_code.toy", &sourceLength);
|
||||||
|
|
||||||
//test basic compilation & collation
|
//test basic compilation & collation
|
||||||
Lexer lexer;
|
Lexer lexer;
|
||||||
|
|||||||
+28
-5
@@ -16,6 +16,18 @@ static void noPrintFn(const char* output) {
|
|||||||
//NO OP
|
//NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ignoredAssertions = 0;
|
||||||
|
static void noAssertFn(const char* output) {
|
||||||
|
if (strncmp(output, "!ignore", 7) == 0) {
|
||||||
|
ignoredAssertions++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, ERROR "Assertion failure: ");
|
||||||
|
fprintf(stderr, "%s", output);
|
||||||
|
fprintf(stderr, "\n" RESET); //default new line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//compilation functions
|
//compilation functions
|
||||||
char* readFile(char* path, size_t* fileSize) {
|
char* readFile(char* path, size_t* fileSize) {
|
||||||
FILE* file = fopen(path, "rb");
|
FILE* file = fopen(path, "rb");
|
||||||
@@ -94,6 +106,7 @@ void runBinary(unsigned char* tb, size_t size) {
|
|||||||
|
|
||||||
//NOTE: suppress print output for testing
|
//NOTE: suppress print output for testing
|
||||||
setInterpreterPrint(&interpreter, noPrintFn);
|
setInterpreterPrint(&interpreter, noPrintFn);
|
||||||
|
setInterpreterAssert(&interpreter, noAssertFn);
|
||||||
|
|
||||||
runInterpreter(&interpreter, tb, size);
|
runInterpreter(&interpreter, tb, size);
|
||||||
freeInterpreter(&interpreter);
|
freeInterpreter(&interpreter);
|
||||||
@@ -147,8 +160,9 @@ int main() {
|
|||||||
int size = 0;
|
int size = 0;
|
||||||
unsigned char* bytecode = collateCompiler(&compiler, &size);
|
unsigned char* bytecode = collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
//NOTE: supress print output for testing
|
//NOTE: suppress print output for testing
|
||||||
setInterpreterPrint(&interpreter, noPrintFn);
|
setInterpreterPrint(&interpreter, noPrintFn);
|
||||||
|
setInterpreterAssert(&interpreter, noAssertFn);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
runInterpreter(&interpreter, bytecode, size);
|
runInterpreter(&interpreter, bytecode, size);
|
||||||
@@ -161,14 +175,16 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
//run each file in ../scripts/test/
|
//run each file in tests/scripts/
|
||||||
char* filenames[] = {
|
char* filenames[] = {
|
||||||
"arithmetic.toy",
|
"arithmetic.toy",
|
||||||
"casting.toy",
|
"casting.toy",
|
||||||
"coercions.toy",
|
"coercions.toy",
|
||||||
"comparisons.toy",
|
"comparisons.toy",
|
||||||
"dot-and-matrix.toy",
|
"dot-and-matrix.toy",
|
||||||
|
"dot-assignments-bugfix.toy",
|
||||||
"dot-chaining.toy",
|
"dot-chaining.toy",
|
||||||
|
"dottify-bugfix.toy",
|
||||||
"functions.toy",
|
"functions.toy",
|
||||||
"imports-and-exports.toy",
|
"imports-and-exports.toy",
|
||||||
"index-arrays.toy",
|
"index-arrays.toy",
|
||||||
@@ -190,7 +206,7 @@ int main() {
|
|||||||
printf("Running %s\n", filenames[i]);
|
printf("Running %s\n", filenames[i]);
|
||||||
|
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
snprintf(buffer, 128, "../scripts/test/%s", filenames[i]);
|
snprintf(buffer, 128, "scripts/%s", filenames[i]);
|
||||||
|
|
||||||
runSourceFile(buffer);
|
runSourceFile(buffer);
|
||||||
}
|
}
|
||||||
@@ -200,8 +216,8 @@ int main() {
|
|||||||
//read source
|
//read source
|
||||||
size_t dummy;
|
size_t dummy;
|
||||||
size_t exportSize, importSize;
|
size_t exportSize, importSize;
|
||||||
char* exportSource = readFile("../scripts/test/separate-exports.toy", &dummy);
|
char* exportSource = readFile("scripts/separate-exports.toy", &dummy);
|
||||||
char* importSource = readFile("../scripts/test/separate-imports.toy", &dummy);
|
char* importSource = readFile("scripts/separate-imports.toy", &dummy);
|
||||||
|
|
||||||
//compile
|
//compile
|
||||||
unsigned char* exportBinary = compileString(exportSource, &exportSize);
|
unsigned char* exportBinary = compileString(exportSource, &exportSize);
|
||||||
@@ -213,6 +229,7 @@ int main() {
|
|||||||
|
|
||||||
//NOTE: supress print output for testing
|
//NOTE: supress print output for testing
|
||||||
setInterpreterPrint(&interpreter, noPrintFn);
|
setInterpreterPrint(&interpreter, noPrintFn);
|
||||||
|
setInterpreterAssert(&interpreter, noAssertFn);
|
||||||
|
|
||||||
runInterpreter(&interpreter, exportBinary, exportSize); //automatically frees the binary data
|
runInterpreter(&interpreter, exportBinary, exportSize); //automatically frees the binary data
|
||||||
|
|
||||||
@@ -227,6 +244,12 @@ int main() {
|
|||||||
free((void*)importSource);
|
free((void*)importSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//1, to allow for the assertion test
|
||||||
|
if (ignoredAssertions > 1) {
|
||||||
|
fprintf(stderr, ERROR "Assertions hidden: %d\n", ignoredAssertions);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
printf(NOTICE "All good\n" RESET);
|
printf(NOTICE "All good\n" RESET);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-28
@@ -12,12 +12,26 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../repl/lib_standard.h"
|
#include "../repl/lib_standard.h"
|
||||||
|
#include "../repl/lib_timer.h"
|
||||||
|
|
||||||
//supress the print output
|
//supress the print output
|
||||||
static void noPrintFn(const char* output) {
|
static void noPrintFn(const char* output) {
|
||||||
//NO OP
|
//NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int failedAsserts = 0;
|
||||||
|
static void assertWrapper(const char* output) {
|
||||||
|
failedAsserts++;
|
||||||
|
fprintf(stderr, ERROR "Assertion failure: ");
|
||||||
|
fprintf(stderr, "%s", output);
|
||||||
|
fprintf(stderr, "\n" RESET); //default new line
|
||||||
|
}
|
||||||
|
|
||||||
|
static void errorWrapper(const char* output) {
|
||||||
|
failedAsserts++;
|
||||||
|
fprintf(stderr, ERROR "%s" RESET, output);
|
||||||
|
}
|
||||||
|
|
||||||
//compilation functions
|
//compilation functions
|
||||||
char* readFile(char* path, size_t* fileSize) {
|
char* readFile(char* path, size_t* fileSize) {
|
||||||
FILE* file = fopen(path, "rb");
|
FILE* file = fopen(path, "rb");
|
||||||
@@ -90,56 +104,65 @@ unsigned char* compileString(char* source, size_t* size) {
|
|||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void runBinary(unsigned char* tb, size_t size) {
|
void runBinaryWithLibrary(unsigned char* tb, size_t size, char* library, HookFn hook) {
|
||||||
Interpreter interpreter;
|
Interpreter interpreter;
|
||||||
initInterpreter(&interpreter);
|
initInterpreter(&interpreter);
|
||||||
|
|
||||||
//NOTE: supress print output for testing
|
//NOTE: supress print output for testing
|
||||||
setInterpreterPrint(&interpreter, noPrintFn);
|
setInterpreterPrint(&interpreter, noPrintFn);
|
||||||
|
setInterpreterAssert(&interpreter, assertWrapper);
|
||||||
|
setInterpreterError(&interpreter, errorWrapper);
|
||||||
|
|
||||||
//inject the standard libraries into this interpreter
|
//inject the standard libraries into this interpreter
|
||||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
injectNativeHook(&interpreter, library, hook);
|
||||||
|
|
||||||
runInterpreter(&interpreter, tb, size);
|
runInterpreter(&interpreter, tb, size);
|
||||||
freeInterpreter(&interpreter);
|
freeInterpreter(&interpreter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runSource(char* source) {
|
typedef struct Payload {
|
||||||
size_t size = 0;
|
char* fname;
|
||||||
unsigned char* tb = compileString(source, &size);
|
char* libname;
|
||||||
if (!tb) {
|
HookFn hook;
|
||||||
return;
|
} Payload;
|
||||||
}
|
|
||||||
runBinary(tb, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void runSourceFile(char* fname) {
|
|
||||||
size_t size = 0; //not used
|
|
||||||
char* source = readFile(fname, &size);
|
|
||||||
runSource(source);
|
|
||||||
free((void*)source);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
{
|
{
|
||||||
//run each file in ../scripts/test/
|
//run each file in test/scripts
|
||||||
char* filenames[] = {
|
Payload payloads[] = {
|
||||||
"interactions.toy",
|
{"interactions.toy", "standard", hookStandard}, //interactions needs standard
|
||||||
"standard.toy",
|
{"standard.toy", "standard", hookStandard},
|
||||||
NULL
|
{"timer.toy", "timer", hookTimer},
|
||||||
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; filenames[i]; i++) {
|
for (int i = 0; payloads[i].fname; i++) {
|
||||||
printf("Running %s\n", filenames[i]);
|
printf("Running %s\n", payloads[i].fname);
|
||||||
|
|
||||||
char buffer[128];
|
char fname[128];
|
||||||
snprintf(buffer, 128, "../scripts/test/lib/%s", filenames[i]);
|
snprintf(fname, 128, "scripts/lib/%s", payloads[i].fname);
|
||||||
|
|
||||||
runSourceFile(buffer);
|
//compile the source
|
||||||
|
size_t size = 0;
|
||||||
|
char* source = readFile(fname, &size);
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
free((void*)source);
|
||||||
|
|
||||||
|
if (!tb) {
|
||||||
|
printf(ERROR "Failed to compile file: %s" RESET, fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
runBinaryWithLibrary(tb, size, payloads[i].libname, payloads[i].hook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!failedAsserts) {
|
||||||
printf(NOTICE "All good\n" RESET);
|
printf(NOTICE "All good\n" RESET);
|
||||||
return 0;
|
}
|
||||||
|
else {
|
||||||
|
printf(WARN "Problems detected in libraries\n" RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedAsserts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ unsigned char* compileString(char* source, size_t* size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void error(char* msg) {
|
void error(char* msg) {
|
||||||
printf(msg);
|
printf("%s", msg);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ static int consume(Interpreter* interpreter, LiteralArray* arguments) {
|
|||||||
int main() {
|
int main() {
|
||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char* source = readFile("../scripts/test/opaque-data-type.toy", &size);
|
char* source = readFile("scripts/opaque-data-type.toy", &size);
|
||||||
unsigned char* tb = compileString(source, &size);
|
unsigned char* tb = compileString(source, &size);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -90,7 +90,7 @@ int main() {
|
|||||||
{
|
{
|
||||||
//get the source file
|
//get the source file
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char* source = readFile("sample_code.toy", &size);
|
char* source = readFile("scripts/sample_code.toy", &size);
|
||||||
|
|
||||||
//test parsing a chunk of junk (valgrind will find leaks)
|
//test parsing a chunk of junk (valgrind will find leaks)
|
||||||
Lexer lexer;
|
Lexer lexer;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// A launch configuration that launches the extension inside a new window
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Extension",
|
||||||
|
"type": "extensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"args": [
|
||||||
|
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.vscode/**
|
||||||
|
.vscode-test/**
|
||||||
|
.gitignore
|
||||||
|
vsc-extension-quickstart.md
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Toy VSCode Highlighting
|
||||||
|
|
||||||
|
This is included in the core repo under `/tools`, and is supposed to make your time writing Toy code easier.
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
Just copy the whole folder into the extensions folder for VSCode, or run `make install-tools` in the root directory of Toy.
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"comments": {
|
||||||
|
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
||||||
|
"lineComment": "//",
|
||||||
|
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
|
||||||
|
"blockComment": [ "/*", "*/" ]
|
||||||
|
},
|
||||||
|
// symbols used as brackets
|
||||||
|
"brackets": [
|
||||||
|
["{", "}"],
|
||||||
|
["[", "]"],
|
||||||
|
["(", ")"]
|
||||||
|
],
|
||||||
|
// symbols that are auto closed when typing
|
||||||
|
"autoClosingPairs": [
|
||||||
|
["{", "}"],
|
||||||
|
["[", "]"],
|
||||||
|
["(", ")"],
|
||||||
|
["\"", "\""],
|
||||||
|
],
|
||||||
|
// symbols that can be used to surround a selection
|
||||||
|
"surroundingPairs": [
|
||||||
|
["{", "}"],
|
||||||
|
["[", "]"],
|
||||||
|
["(", ")"],
|
||||||
|
["\"", "\""],
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "toy-syntax-highlighting",
|
||||||
|
"displayName": "Toy Syntax Highlighting",
|
||||||
|
"description": "Syntax highligher for a toy programming language",
|
||||||
|
"author": "Kayne Ruse",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.73.0"
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
"Programming Languages"
|
||||||
|
],
|
||||||
|
"contributes": {
|
||||||
|
"languages": [{
|
||||||
|
"id": "toy",
|
||||||
|
"aliases": ["Toy", "toy"],
|
||||||
|
"extensions": [".toy"],
|
||||||
|
"configuration": "./language-configuration.json"
|
||||||
|
}],
|
||||||
|
"grammars": [{
|
||||||
|
"language": "toy",
|
||||||
|
"scopeName": "source.toy",
|
||||||
|
"path": "./syntaxes/toy.tmLanguage.json"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||||
|
"name": "Toy",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"include": "#comments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#keywords"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#strings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#numbers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#booleans"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#reserved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"comments": {
|
||||||
|
"patterns": [{
|
||||||
|
"name": "comment.line.toy",
|
||||||
|
"begin": "\/\/",
|
||||||
|
"end": "\\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "comment.block.toy",
|
||||||
|
"begin": "/\\*",
|
||||||
|
"end": "\\*/"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"patterns": [{
|
||||||
|
"name": "keyword.control.toy",
|
||||||
|
"match": "\\b(if|else|while|for|return|break|continue)\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.name.type.toy",
|
||||||
|
"match": "\\b(any|bool|const|float|int|opaque|string|type)\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.other.toy",
|
||||||
|
"match": "\\b(as|astype|assert|export|fn|import|print|typeof|var)\\b"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"strings": {
|
||||||
|
"name": "string.quoted.double.toy",
|
||||||
|
"begin": "\"",
|
||||||
|
"end": "\""
|
||||||
|
},
|
||||||
|
"numbers": {
|
||||||
|
"patterns": [{
|
||||||
|
"match": "[-]?[0-9]+(.[0-9]+)?",
|
||||||
|
"name": "constant.numeric.toy"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"booleans": {
|
||||||
|
"patterns": [{
|
||||||
|
"match": "\\b(true|false)\\b",
|
||||||
|
"name": "constant.numeric.toy"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"null": {
|
||||||
|
"patterns": [{
|
||||||
|
"match": "\\b(null)\\b",
|
||||||
|
"name": "constant.numeric.toy"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"reserved": {
|
||||||
|
"patterns": [{
|
||||||
|
"name": "keyword.reserved.toy",
|
||||||
|
"match": "\\b(class|do|foreach|in|of)\\b"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scopeName": "source.toy"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user