Compare commits

..

28 Commits

Author SHA1 Message Date
Kayne Ruse 7bf18a744c Tweaked bounds check 2022-11-12 11:39:32 +00:00
Kayne Ruse fa20763c07 Corrected negative timers 2022-11-12 11:30:24 +00:00
Kayne Ruse 774f3d9e83 Corrected error messages in the timer lib 2022-11-12 10:07:54 +00:00
Kayne Ruse 2d18ff4ba3 Fixed some printf formats in the tests 2022-11-12 03:26:14 +00:00
Kayne Ruse 30b068fcdb Fixed timer issues, tests should pass
Reduced test duration by a factor of 11,000. Don't ask.

Also, something funny is going on with the time headers, so I
stuck them into source/toy_common.h, I'll figure it out later.
2022-11-12 04:45:35 +11:00
Kayne Ruse 3d8ce4e7d8 Trying to fix time and platforms 2022-11-11 17:31:45 +00:00
Kayne Ruse e0ab4106fa Implemented the timer library 2022-11-11 17:18:07 +00:00
Kayne Ruse 2c143a8be5 Moved tests from scripts/ to test/scripts/ 2022-11-11 14:51:47 +00:00
Kayne Ruse 0aa6e4063b Spotted a scope issue in the test 2022-11-08 20:22:20 +00:00
Kayne Ruse ec39f099ca Wrote failing TDD test for timer library, not enabled 2022-11-08 19:40:21 +00:00
Kayne Ruse 4dcc05e796 Corrected fnv1 hash algorithm 2022-11-08 19:36:54 +00:00
Kayne Ruse 2af95ec82e Tweak 2022-11-08 02:55:02 +00:00
Kayne Ruse bbdb521333 Merge remote-tracking branch 'refs/remotes/origin/main' 2022-11-08 02:53:07 +00:00
Kayne Ruse 56987bc96a Tweaked colors for types 2022-11-08 02:52:18 +00:00
Kayne Ruse 8498864dde Resolved a name clash with the engine 2022-11-07 16:38:39 +00:00
Kayne Ruse 14710dec90 Tweaked README.md 2022-11-07 10:12:15 +00:00
Kayne Ruse d14177dbca Made it easier to install syntax highlighting 2022-11-07 10:02:37 +00:00
Kayne Ruse 42580bbe2a Update toy.tmLanguage.json
Forgot opaque type
2022-11-07 20:47:37 +11:00
Kayne Ruse 0c8e036de8 Added vscode syntax highlighting under /tools 2022-11-07 09:44:26 +00:00
Kayne Ruse a55338d8e3 Tweaked README.md 2022-11-06 04:15:33 +00:00
Kayne Ruse 5d240f85a6 BUGFIX: chained calls not being dottified 2022-11-04 11:13:40 +01:00
Kayne Ruse cceefa6375 Resolved #38 2022-11-03 16:25:29 +01:00
Kayne Ruse 632ed7c089 The tests did not like that 2022-11-03 22:59:25 +11:00
Kayne Ruse c1528f5501 Parallelized compilation 2022-11-03 12:54:41 +01:00
Kayne Ruse 6c5d952c44 Tests are ok for now 2022-10-20 00:00:13 +01:00
Kayne Ruse 208ad9d615 Experimenting 2022-10-20 09:45:28 +11:00
Kayne Ruse 876aad853c Creating the CI file 2022-10-20 09:42:52 +11:00
Kayne Ruse 1baa65cc95 Removed annoying assertion test messages from test output 2022-10-19 23:34:15 +01:00
72 changed files with 881 additions and 131 deletions
+19
View File
@@ -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
+7 -1
View File
@@ -2,7 +2,7 @@
<image src="toylogo.png" />
</p>
# Toy 0.6.0
# Toy
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!
## Tools
Run `make install-tools` to install a number of tools, including:
* VSCode syntax highlighting
## Syntax
```
+6 -2
View File
@@ -12,10 +12,10 @@ repl-static: $(TOY_OUTDIR) static
$(MAKE) -C repl
library: $(TOY_OUTDIR)
$(MAKE) -C source library
$(MAKE) -j8 -C source library
static: $(TOY_OUTDIR)
$(MAKE) -C source static
$(MAKE) -j8 -C source static
test: clean $(TOY_OUTDIR)
$(MAKE) -C test
@@ -23,6 +23,10 @@ test: clean $(TOY_OUTDIR)
$(TOY_OUTDIR):
mkdir $(TOY_OUTDIR)
#utils
install-tools:
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
.PHONY: clean
clean:
+1 -1
View File
@@ -2,7 +2,7 @@
#include "memory.h"
#include <time.h>
#include <sys/time.h>
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
+411
View File
@@ -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, &microsecondLiteral)) {
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;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "interpreter.h"
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
+2
View File
@@ -1,5 +1,6 @@
#include "repl_tools.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "console_colors.h"
@@ -25,6 +26,7 @@ void repl() {
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
for(;;) {
printf("> ");
+2
View File
@@ -1,5 +1,6 @@
#include "repl_tools.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "console_colors.h"
@@ -108,6 +109,7 @@ void runBinary(unsigned char* tb, size_t size) {
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter);
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
char* readFile(char* path, size_t* fileSize);
void writeFile(char* path, unsigned char* bytes, size_t size);
+2 -2
View File
@@ -6,7 +6,7 @@ fn fib(n : int) {
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);
print (string)i + ": " + (string)res;
print string i + ": " + string res;
}
+9 -52
View File
@@ -1,56 +1,13 @@
import timer;
var a = createTimer(1, 0);
var b = createTimer(2, 0);
fn _b(self) {
print "running _b";
print self;
return self;
}
print a.compareTimer(b).timerToString();
print b.compareTimer(a).timerToString();
fn _c(self) {
print "running _c";
print self;
return self;
}
var c = createTimer(0, 1);
var d = createTimer(0, 2);
fn _d(self) {
print "running _d";
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();
print c.compareTimer(d).timerToString();
print d.compareTimer(c).timerToString();
-8
View File
@@ -1,8 +0,0 @@
fn panic() {
assert false, "This should only be seen once";
}
panic();
panic();
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "literal.h"
#include "opcodes.h"
#include "token_types.h"
+1 -1
View File
@@ -891,7 +891,7 @@ int _index(Interpreter* interpreter, LiteralArray* arguments) {
//else override elements of the array instead
else {
//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 min = AS_INTEGER(third) > 0 ? AS_INTEGER(first) : AS_INTEGER(second) - 1;
+1 -1
View File
@@ -333,7 +333,7 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, ASTNode* node, void* br
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
ret = OP_EOF; //untangle in this case
}
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "opcodes.h"
#include "ast_node.h"
#include "literal_array.h"
+1 -1
View File
@@ -1,7 +1,7 @@
#include "interpreter.h"
#include "console_colors.h"
#include "common.h"
#include "toy_common.h"
#include "memory.h"
#include "keyword_types.h"
#include "opcodes.h"
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "literal.h"
#include "literal_array.h"
#include "literal_dictionary.h"
+1 -1
View File
@@ -1,6 +1,6 @@
#include "keyword_types.h"
#include "common.h"
#include "toy_common.h"
#include <string.h>
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "token_types.h"
//lexers are bound to a string of code, and return a single token every time scan is called
+1 -1
View File
@@ -15,7 +15,7 @@ static unsigned int hashString(const char* string, int length) {
for (int i = 0; i < length; i++) {
hash *= string[i];
hash *= 16777619;
hash ^= 16777619;
}
return hash;
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include <string.h>
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "literal.h"
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "literal.h"
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
+1 -8
View File
@@ -1,7 +1,5 @@
#include "parser.h"
#include "common.h"
#include "memory.h"
#include "literal.h"
#include "opcodes.h"
@@ -798,12 +796,6 @@ static Opcode dot(Parser* parser, ASTNode** nodeHandle) {
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;
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)->binary.opcode == OP_FN_CALL) {
(*nodeHandle)->binary.opcode = OP_DOT;
(*nodeHandle)->binary.right->fnCall.argumentCount++;
}
dottify(parser, &(*nodeHandle)->binary.left);
dottify(parser, &(*nodeHandle)->binary.right);
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common.h"
#include "toy_common.h"
#include "lexer.h"
#include "ast_node.h"
+1 -1
View File
@@ -1,4 +1,4 @@
#include "common.h"
#include "toy_common.h"
#include <stdio.h>
#include <string.h>
+12 -1
View File
@@ -6,14 +6,25 @@
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 6
#define TOY_VERSION_PATCH 0
#define TOY_VERSION_PATCH 2
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
//platform exports/imports
#if defined(__linux__)
#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
#define TOY_API
#include <time.h>
#include <sys/time.h>
#endif
#ifndef TOY_EXPORT
+20
View File
@@ -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";
+9
View File
@@ -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";
+92
View File
@@ -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";
+8
View File
@@ -0,0 +1,8 @@
fn panic() {
assert false, "!ignore panicking within a function";
}
panic();
panic();
+4 -2
View File
@@ -90,14 +90,14 @@ unsigned char* compileString(char* source, size_t* size) {
}
void error(char* msg) {
printf(msg);
printf("%s", msg);
exit(-1);
}
int main() {
{
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);
free((void*)source);
@@ -281,6 +281,8 @@ int main() {
{
interpreter.printOutput("Testing assertion failure");
setInterpreterAssert(&interpreter, noPrintFn);
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
+1 -1
View File
@@ -84,7 +84,7 @@ int main() {
{
//source
size_t sourceLength = 0;
char* source = readFile("sample_code.toy", &sourceLength);
char* source = readFile("scripts/sample_code.toy", &sourceLength);
//test basic compilation & collation
Lexer lexer;
+28 -5
View File
@@ -16,6 +16,18 @@ static void noPrintFn(const char* output) {
//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
char* readFile(char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
@@ -94,6 +106,7 @@ void runBinary(unsigned char* tb, size_t size) {
//NOTE: suppress print output for testing
setInterpreterPrint(&interpreter, noPrintFn);
setInterpreterAssert(&interpreter, noAssertFn);
runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter);
@@ -147,8 +160,9 @@ int main() {
int size = 0;
unsigned char* bytecode = collateCompiler(&compiler, &size);
//NOTE: supress print output for testing
//NOTE: suppress print output for testing
setInterpreterPrint(&interpreter, noPrintFn);
setInterpreterAssert(&interpreter, noAssertFn);
//run
runInterpreter(&interpreter, bytecode, size);
@@ -161,14 +175,16 @@ int main() {
}
{
//run each file in ../scripts/test/
//run each file in tests/scripts/
char* filenames[] = {
"arithmetic.toy",
"casting.toy",
"coercions.toy",
"comparisons.toy",
"dot-and-matrix.toy",
"dot-assignments-bugfix.toy",
"dot-chaining.toy",
"dottify-bugfix.toy",
"functions.toy",
"imports-and-exports.toy",
"index-arrays.toy",
@@ -190,7 +206,7 @@ int main() {
printf("Running %s\n", filenames[i]);
char buffer[128];
snprintf(buffer, 128, "../scripts/test/%s", filenames[i]);
snprintf(buffer, 128, "scripts/%s", filenames[i]);
runSourceFile(buffer);
}
@@ -200,8 +216,8 @@ int main() {
//read source
size_t dummy;
size_t exportSize, importSize;
char* exportSource = readFile("../scripts/test/separate-exports.toy", &dummy);
char* importSource = readFile("../scripts/test/separate-imports.toy", &dummy);
char* exportSource = readFile("scripts/separate-exports.toy", &dummy);
char* importSource = readFile("scripts/separate-imports.toy", &dummy);
//compile
unsigned char* exportBinary = compileString(exportSource, &exportSize);
@@ -213,6 +229,7 @@ int main() {
//NOTE: supress print output for testing
setInterpreterPrint(&interpreter, noPrintFn);
setInterpreterAssert(&interpreter, noAssertFn);
runInterpreter(&interpreter, exportBinary, exportSize); //automatically frees the binary data
@@ -227,6 +244,12 @@ int main() {
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);
return 0;
}
+52 -29
View File
@@ -12,12 +12,26 @@
#include <string.h>
#include "../repl/lib_standard.h"
#include "../repl/lib_timer.h"
//supress the print output
static void noPrintFn(const char* output) {
//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
char* readFile(char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
@@ -90,56 +104,65 @@ unsigned char* compileString(char* source, size_t* size) {
return tb;
}
void runBinary(unsigned char* tb, size_t size) {
void runBinaryWithLibrary(unsigned char* tb, size_t size, char* library, HookFn hook) {
Interpreter interpreter;
initInterpreter(&interpreter);
//NOTE: supress print output for testing
setInterpreterPrint(&interpreter, noPrintFn);
setInterpreterAssert(&interpreter, assertWrapper);
setInterpreterError(&interpreter, errorWrapper);
//inject the standard libraries into this interpreter
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, library, hook);
runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter);
}
void runSource(char* source) {
size_t size = 0;
unsigned char* tb = compileString(source, &size);
if (!tb) {
return;
}
runBinary(tb, size);
}
void runSourceFile(char* fname) {
size_t size = 0; //not used
char* source = readFile(fname, &size);
runSource(source);
free((void*)source);
}
typedef struct Payload {
char* fname;
char* libname;
HookFn hook;
} Payload;
int main() {
{
//run each file in ../scripts/test/
char* filenames[] = {
"interactions.toy",
"standard.toy",
NULL
//run each file in test/scripts
Payload payloads[] = {
{"interactions.toy", "standard", hookStandard}, //interactions needs standard
{"standard.toy", "standard", hookStandard},
{"timer.toy", "timer", hookTimer},
{NULL, NULL, NULL}
};
for (int i = 0; filenames[i]; i++) {
printf("Running %s\n", filenames[i]);
for (int i = 0; payloads[i].fname; i++) {
printf("Running %s\n", payloads[i].fname);
char buffer[128];
snprintf(buffer, 128, "../scripts/test/lib/%s", filenames[i]);
char fname[128];
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);
}
}
printf(NOTICE "All good\n" RESET);
return 0;
if (!failedAsserts) {
printf(NOTICE "All good\n" RESET);
}
else {
printf(WARN "Problems detected in libraries\n" RESET);
}
return failedAsserts;
}
+2 -2
View File
@@ -88,7 +88,7 @@ unsigned char* compileString(char* source, size_t* size) {
}
void error(char* msg) {
printf(msg);
printf("%s", msg);
exit(-1);
}
@@ -138,7 +138,7 @@ static int consume(Interpreter* interpreter, LiteralArray* arguments) {
int main() {
{
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);
free((void*)source);
+1 -1
View File
@@ -90,7 +90,7 @@ int main() {
{
//get the source file
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)
Lexer lexer;
+17
View File
@@ -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"
}