Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3498baad9b | |||
| 0649a141dd | |||
| 65e5905011 | |||
| bf64275aa9 | |||
| 08b400debf | |||
| 9ad2a6cf2e | |||
| 8009f410a4 | |||
| 584fb115b6 | |||
| 70698a4a1a | |||
| eb26d23363 | |||
| c1625c53f4 | |||
| fb55f42d0e |
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: krgamestudios
|
||||
ko_fi: krgamestudios
|
||||
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# License
|
||||
|
||||
Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios
|
||||
Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
@@ -57,7 +58,7 @@ int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Literal name = TO_STRING_LITERAL(createRefStringLength(natives[i].name, strlen(natives[i].name)));
|
||||
Literal name = TO_STRING_LITERAL(createRefString(natives[i].name));
|
||||
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
#include "lib_timer.h"
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
|
||||
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
|
||||
@@ -374,7 +375,7 @@ int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Literal name = TO_STRING_LITERAL(createRefStringLength(natives[i].name, strlen(natives[i].name)));
|
||||
Literal name = TO_STRING_LITERAL(createRefString(natives[i].name));
|
||||
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ int main(int argc, const char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: remove this when the interpreter meets the specification
|
||||
//version
|
||||
if (command.verbose) {
|
||||
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||
}
|
||||
|
||||
+40
-57
@@ -1,66 +1,49 @@
|
||||
var size: int const = 100;
|
||||
//number of iterations
|
||||
var SIZE: int const = 100;
|
||||
|
||||
var prev = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
prev.push(false);
|
||||
//lookup table
|
||||
var lookup = [
|
||||
"*": [
|
||||
"*": [
|
||||
"*": " ",
|
||||
" ": "*"
|
||||
],
|
||||
" ": [
|
||||
"*": "*",
|
||||
" ": " "
|
||||
]
|
||||
], " ": [
|
||||
"*": [
|
||||
"*": "*",
|
||||
" ": "*"
|
||||
],
|
||||
" ": [
|
||||
"*": "*",
|
||||
" ": " "
|
||||
]
|
||||
]];
|
||||
|
||||
//initial line to build from
|
||||
var prev: string = "";
|
||||
for (var i = 0; i < SIZE -1; i++) {
|
||||
prev += " ";
|
||||
}
|
||||
prev += "*"; //initial
|
||||
print prev;
|
||||
|
||||
prev.set(size - 1, true);
|
||||
|
||||
|
||||
fn calc(p, i) {
|
||||
if (p[i-1] && p[i] && p[i+1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p[i-1] && p[i] && !p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p[i-1] && !p[i] && p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p[i-1] && !p[i] && !p[i+1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p[i-1] && p[i] && p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p[i-1] && p[i] && !p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p[i-1] && !p[i] && p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p[i-1] && !p[i] && !p[i+1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//run
|
||||
for (var iteration = 0; iteration < 100; iteration++) {
|
||||
var line = [false];
|
||||
for (var i = 1; i < size-1; i++) {
|
||||
line.push(calc(prev, i));
|
||||
}
|
||||
line.push(false);
|
||||
//left
|
||||
var output = (lookup[" "][prev[0]][prev[1]]);
|
||||
|
||||
var output = "";
|
||||
for (var i = 0; i < line.length(); i++) {
|
||||
if (line[i]) {
|
||||
output += "*";
|
||||
}
|
||||
else {
|
||||
output += " ";
|
||||
}
|
||||
//middle
|
||||
for (var i = 1; i < SIZE-1; i++) {
|
||||
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
|
||||
}
|
||||
|
||||
//right
|
||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||
|
||||
print output;
|
||||
prev = line;
|
||||
prev = output;
|
||||
}
|
||||
|
||||
+8
-11
@@ -1,13 +1,10 @@
|
||||
import timer;
|
||||
|
||||
var a = createTimer(1, 0);
|
||||
var b = createTimer(2, 0);
|
||||
{
|
||||
var t = astype [int];
|
||||
var arr: t = [1, 2, 3.14];
|
||||
}
|
||||
|
||||
print a.compareTimer(b).timerToString();
|
||||
print b.compareTimer(a).timerToString();
|
||||
|
||||
var c = createTimer(0, 1);
|
||||
var d = createTimer(0, 2);
|
||||
|
||||
print c.compareTimer(d).timerToString();
|
||||
print d.compareTimer(c).timerToString();
|
||||
{
|
||||
var t = astype [string:int];
|
||||
var dict: t = ["one": 1, "two": 2, "pi": 3.14];
|
||||
}
|
||||
|
||||
@@ -129,11 +129,6 @@ void freeASTNodeCustom(ASTNode* node, bool freeSelf) {
|
||||
freeLiteral(node->import.identifier);
|
||||
freeLiteral(node->import.alias);
|
||||
break;
|
||||
|
||||
case AST_NODE_EXPORT:
|
||||
freeLiteral(node->export.identifier);
|
||||
freeLiteral(node->export.alias);
|
||||
break;
|
||||
}
|
||||
|
||||
if (freeSelf) {
|
||||
@@ -371,13 +366,3 @@ void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias)
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeExport(ASTNode** nodeHandle, Literal identifier, Literal alias) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_EXPORT;
|
||||
tmp->export.identifier = copyLiteral(identifier);
|
||||
tmp->export.alias = copyLiteral(alias);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
+3
-12
@@ -32,8 +32,7 @@ typedef enum ASTNodeType {
|
||||
AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||
AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||
AST_NODE_POSTFIX_DECREMENT, //decrement a variable
|
||||
AST_NODE_IMPORT, //import a variable
|
||||
AST_NODE_EXPORT, //export a variable
|
||||
AST_NODE_IMPORT, //import a library
|
||||
} ASTNodeType;
|
||||
|
||||
//literals
|
||||
@@ -219,9 +218,8 @@ typedef struct NodePostfixDecrement {
|
||||
Literal identifier;
|
||||
} NodePostfixDecrement;
|
||||
|
||||
//import/export a variable
|
||||
//import a library
|
||||
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias);
|
||||
void emitASTNodeExport(ASTNode** nodeHandle, Literal identifier, Literal alias);
|
||||
|
||||
typedef struct NodeImport {
|
||||
ASTNodeType type;
|
||||
@@ -229,12 +227,6 @@ typedef struct NodeImport {
|
||||
Literal alias;
|
||||
} NodeImport;
|
||||
|
||||
typedef struct NodeExport {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
Literal alias;
|
||||
} NodeExport;
|
||||
|
||||
union _node {
|
||||
ASTNodeType type;
|
||||
NodeLiteral atomic;
|
||||
@@ -250,7 +242,7 @@ union _node {
|
||||
NodeFnDecl fnDecl;
|
||||
NodeFnCall fnCall;
|
||||
NodeFnReturn returns;
|
||||
NodeIf pathIf; //TODO: rename these to ifStmt?
|
||||
NodeIf pathIf;
|
||||
NodeWhile pathWhile;
|
||||
NodeFor pathFor;
|
||||
NodeBreak pathBreak;
|
||||
@@ -260,7 +252,6 @@ union _node {
|
||||
NodePostfixIncrement postfixIncrement;
|
||||
NodePostfixDecrement postfixDecrement;
|
||||
NodeImport import;
|
||||
NodeExport export;
|
||||
};
|
||||
|
||||
TOY_API void freeASTNode(ASTNode* node);
|
||||
|
||||
+5
-28
@@ -18,7 +18,7 @@ void initCompiler(Compiler* compiler) {
|
||||
}
|
||||
|
||||
//separated out, so it can be recursive
|
||||
static int writeLiteralTypeToCacheOpt(LiteralArray* literalCache, Literal literal, bool skipDuplicationOptimisation) {
|
||||
static int writeLiteralTypeToCache(LiteralArray* literalCache, Literal literal) {
|
||||
bool shouldFree = false;
|
||||
|
||||
//if it's a compound type, recurse and store the results
|
||||
@@ -32,7 +32,7 @@ static int writeLiteralTypeToCacheOpt(LiteralArray* literalCache, Literal litera
|
||||
|
||||
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
||||
//write the values to the cache, and the indexes to the store
|
||||
int subIndex = writeLiteralTypeToCacheOpt(literalCache, ((Literal*)(AS_TYPE(literal).subtypes))[i], false);
|
||||
int subIndex = writeLiteralTypeToCache(literalCache, ((Literal*)(AS_TYPE(literal).subtypes))[i]);
|
||||
|
||||
Literal lit = TO_INTEGER_LITERAL(subIndex);
|
||||
pushLiteralArray(store, lit);
|
||||
@@ -45,8 +45,7 @@ static int writeLiteralTypeToCacheOpt(LiteralArray* literalCache, Literal litera
|
||||
literal.type = LITERAL_TYPE_INTERMEDIATE; //NOTE: tweaking the type usually isn't a good idea
|
||||
}
|
||||
|
||||
if (!skipDuplicationOptimisation) {
|
||||
//BUGFIX: check if exactly this literal array exists
|
||||
//optimisation: check if exactly this literal array exists
|
||||
int index = findLiteralIndex(literalCache, literal);
|
||||
if (index < 0) {
|
||||
index = pushLiteralArray(literalCache, literal);
|
||||
@@ -57,18 +56,6 @@ static int writeLiteralTypeToCacheOpt(LiteralArray* literalCache, Literal litera
|
||||
}
|
||||
return index;
|
||||
}
|
||||
else {
|
||||
int index = pushLiteralArray(literalCache, literal);
|
||||
if (shouldFree) {
|
||||
freeLiteral(literal);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
static int writeLiteralTypeToCache(LiteralArray* literalCache, Literal literal) {
|
||||
return writeLiteralTypeToCacheOpt(literalCache, literal, false);
|
||||
}
|
||||
|
||||
static int writeNodeCompoundToCache(Compiler* compiler, ASTNode* node) {
|
||||
int index = -1;
|
||||
@@ -200,7 +187,7 @@ static int writeNodeCollectionToCache(Compiler* compiler, ASTNode* node) {
|
||||
case AST_NODE_VAR_DECL: {
|
||||
//write each piece of the declaration to the cache
|
||||
int identifierIndex = pushLiteralArray(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.identifier); //store without duplication optimisation
|
||||
int typeIndex = writeLiteralTypeToCacheOpt(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.typeLiteral, false);
|
||||
int typeIndex = writeLiteralTypeToCache(&compiler->literalCache, node->fnCollection.nodes[i].varDecl.typeLiteral);
|
||||
|
||||
Literal identifierLiteral = TO_INTEGER_LITERAL(identifierIndex);
|
||||
pushLiteralArray(store, identifierLiteral);
|
||||
@@ -214,7 +201,7 @@ static int writeNodeCollectionToCache(Compiler* compiler, ASTNode* node) {
|
||||
|
||||
case AST_NODE_LITERAL: {
|
||||
//write each piece of the declaration to the cache
|
||||
int typeIndex = writeLiteralTypeToCacheOpt(&compiler->literalCache, node->fnCollection.nodes[i].atomic.literal, false);
|
||||
int typeIndex = writeLiteralTypeToCache(&compiler->literalCache, node->fnCollection.nodes[i].atomic.literal);
|
||||
|
||||
Literal typeLiteral = TO_INTEGER_LITERAL(typeIndex);
|
||||
pushLiteralArray(store, typeLiteral);
|
||||
@@ -859,16 +846,6 @@ static Opcode writeCompilerWithJumps(Compiler* compiler, ASTNode* node, void* br
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_NODE_EXPORT: {
|
||||
//push the identifier, and the alias
|
||||
writeLiteralToCompiler(compiler, node->import.identifier);
|
||||
writeLiteralToCompiler(compiler, node->import.alias);
|
||||
|
||||
//push the import opcode
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)OP_EXPORT; //1 byte
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_NODE_INDEX: {
|
||||
//pass to the child nodes, then embed the opcode
|
||||
|
||||
|
||||
+12
-106
@@ -1227,8 +1227,6 @@ bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* argumen
|
||||
inner.depth = interpreter->depth + 1;
|
||||
inner.panic = false;
|
||||
initLiteralArray(&inner.stack);
|
||||
inner.exports = interpreter->exports;
|
||||
inner.exportTypes = interpreter->exportTypes;
|
||||
inner.hooks = interpreter->hooks;
|
||||
setInterpreterPrint(&inner, interpreter->printOutput);
|
||||
setInterpreterAssert(&inner, interpreter->assertOutput);
|
||||
@@ -1473,7 +1471,16 @@ static bool execImport(Interpreter* interpreter) {
|
||||
Literal identifier = popLiteralArray(&interpreter->stack);
|
||||
|
||||
//access the hooks
|
||||
if (existsLiteralDictionary(interpreter->hooks, identifier)) {
|
||||
if (!existsLiteralDictionary(interpreter->hooks, identifier)) {
|
||||
interpreter->errorOutput("Unknown library name in import statement: ");
|
||||
printLiteralCustom(identifier, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\"\n");
|
||||
|
||||
freeLiteral(alias);
|
||||
freeLiteral(identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
Literal func = getLiteralDictionary(interpreter->hooks, identifier);
|
||||
|
||||
if (!IS_FUNCTION_NATIVE(func)) {
|
||||
@@ -1497,77 +1504,6 @@ static bool execImport(Interpreter* interpreter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Literal lit = getLiteralDictionary(interpreter->exports, identifier);
|
||||
Literal type = getLiteralDictionary(interpreter->exportTypes, identifier);
|
||||
|
||||
//use the alias
|
||||
if (!IS_NULL(alias)) {
|
||||
if (!declareScopeVariable(interpreter->scope, alias, type)) {
|
||||
interpreter->errorOutput("Can't redefine the variable \"");
|
||||
printLiteralCustom(alias, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\"\n");
|
||||
|
||||
freeLiteral(lit);
|
||||
freeLiteral(type);
|
||||
freeLiteral(alias);
|
||||
freeLiteral(identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
setScopeVariable(interpreter->scope, alias, lit, false);
|
||||
}
|
||||
|
||||
//use the original identifier
|
||||
else {
|
||||
if (!declareScopeVariable(interpreter->scope, identifier, type)) {
|
||||
interpreter->errorOutput("Can't redefine the variable \"");
|
||||
printLiteralCustom(identifier, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\"\n");
|
||||
|
||||
freeLiteral(lit);
|
||||
freeLiteral(type);
|
||||
freeLiteral(alias);
|
||||
freeLiteral(identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
setScopeVariable(interpreter->scope, identifier, lit, false);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
freeLiteral(lit);
|
||||
freeLiteral(type);
|
||||
freeLiteral(alias);
|
||||
freeLiteral(identifier);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool execExport(Interpreter* interpreter) {
|
||||
Literal alias = popLiteralArray(&interpreter->stack);
|
||||
Literal identifier = popLiteralArray(&interpreter->stack);
|
||||
|
||||
Literal lit = TO_NULL_LITERAL;
|
||||
|
||||
getScopeVariable(interpreter->scope, identifier, &lit);
|
||||
Literal type = getScopeType(interpreter->scope, identifier);
|
||||
|
||||
if (!IS_NULL(alias)) {
|
||||
setLiteralDictionary(interpreter->exports, alias, lit);
|
||||
setLiteralDictionary(interpreter->exportTypes, alias, type);
|
||||
}
|
||||
else {
|
||||
setLiteralDictionary(interpreter->exports, identifier, lit);
|
||||
setLiteralDictionary(interpreter->exportTypes, identifier, type);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
freeLiteral(lit);
|
||||
freeLiteral(type);
|
||||
freeLiteral(alias);
|
||||
freeLiteral(identifier);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool execIndex(Interpreter* interpreter, bool assignIntermediate) {
|
||||
//assume -> compound, first, second, third are all on the stack
|
||||
|
||||
@@ -1592,7 +1528,8 @@ static bool execIndex(Interpreter* interpreter, bool assignIntermediate) {
|
||||
}
|
||||
|
||||
if (!IS_ARRAY(compound) && !IS_DICTIONARY(compound) && !IS_STRING(compound)) {
|
||||
interpreter->errorOutput("Unknown compound found in indexing notation\n");
|
||||
interpreter->errorOutput("Unknown compound found in indexing notation: ");
|
||||
printLiteralCustom(compound, interpreter->errorOutput);
|
||||
freeLiteral(third);
|
||||
freeLiteral(second);
|
||||
freeLiteral(first);
|
||||
@@ -2086,12 +2023,6 @@ static void execInterpreter(Interpreter* interpreter) {
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_EXPORT:
|
||||
if (!execExport(interpreter)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_INDEX:
|
||||
if (!execIndex(interpreter, false)) {
|
||||
return;
|
||||
@@ -2400,11 +2331,6 @@ static void readInterpreterSections(Interpreter* interpreter) {
|
||||
|
||||
//exposed functions
|
||||
void initInterpreter(Interpreter* interpreter) {
|
||||
//NOTE: separate initialization for exports
|
||||
interpreter->exports = ALLOCATE(LiteralDictionary, 1);
|
||||
initLiteralDictionary(interpreter->exports);
|
||||
interpreter->exportTypes = ALLOCATE(LiteralDictionary, 1);
|
||||
initLiteralDictionary(interpreter->exportTypes);
|
||||
interpreter->hooks = ALLOCATE(LiteralDictionary, 1);
|
||||
initLiteralDictionary(interpreter->hooks);
|
||||
|
||||
@@ -2519,26 +2445,6 @@ void freeInterpreter(Interpreter* interpreter) {
|
||||
interpreter->scope = popScope(interpreter->scope);
|
||||
}
|
||||
|
||||
//BUGFIX: handle scopes/types in the exports
|
||||
for (int i = 0; i < interpreter->exports->capacity; i++) {
|
||||
if (IS_FUNCTION(interpreter->exports->entries[i].key)) {
|
||||
popScope(AS_FUNCTION(interpreter->exports->entries[i].key).scope);
|
||||
AS_FUNCTION(interpreter->exports->entries[i].key).scope = NULL;
|
||||
}
|
||||
if (IS_FUNCTION(interpreter->exports->entries[i].value)) {
|
||||
popScope(AS_FUNCTION(interpreter->exports->entries[i].value).scope);
|
||||
AS_FUNCTION(interpreter->exports->entries[i].value).scope = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
freeLiteralDictionary(interpreter->exports);
|
||||
FREE(LiteralDictionary, interpreter->exports);
|
||||
interpreter->exports = NULL;
|
||||
|
||||
freeLiteralDictionary(interpreter->exportTypes);
|
||||
FREE(LiteralDictionary, interpreter->exportTypes);
|
||||
interpreter->exportTypes = NULL;
|
||||
|
||||
freeLiteralDictionary(interpreter->hooks);
|
||||
FREE(LiteralDictionary, interpreter->hooks);
|
||||
interpreter->hooks = NULL;
|
||||
|
||||
@@ -21,8 +21,7 @@ typedef struct Interpreter {
|
||||
Scope* scope;
|
||||
LiteralArray stack;
|
||||
|
||||
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
|
||||
LiteralDictionary* exportTypes;
|
||||
//Library APIs
|
||||
LiteralDictionary* hooks;
|
||||
|
||||
//debug outputs
|
||||
|
||||
+8
-2
@@ -380,7 +380,7 @@ int hashLiteral(Literal lit) {
|
||||
|
||||
case LITERAL_FUNCTION:
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
return 0; //TODO: find a way to hash these properly
|
||||
return 0; //can't hash these
|
||||
|
||||
case LITERAL_IDENTIFIER:
|
||||
return HASH_I(lit); //pre-computed
|
||||
@@ -448,7 +448,14 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
||||
|
||||
case LITERAL_FLOAT: {
|
||||
char buffer[256];
|
||||
|
||||
if (AS_FLOAT(literal) - (int)AS_FLOAT(literal)) {
|
||||
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, 256, "%.1f", AS_FLOAT(literal));
|
||||
}
|
||||
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
@@ -557,7 +564,6 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
||||
}
|
||||
break;
|
||||
|
||||
//TODO: functions
|
||||
case LITERAL_FUNCTION:
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
printFn("(function)");
|
||||
|
||||
@@ -77,8 +77,6 @@ bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: implicit push when referencing one-past-the-end?
|
||||
|
||||
freeLiteral(array->literals[idx]);
|
||||
array->literals[idx] = copyLiteral(value);
|
||||
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ typedef enum Opcode {
|
||||
OP_TYPE_OF, //get the type of a variable
|
||||
|
||||
OP_IMPORT,
|
||||
OP_EXPORT,
|
||||
OP_EXPORT_removed,
|
||||
|
||||
//for indexing
|
||||
OP_INDEX,
|
||||
|
||||
+2
-39
@@ -418,7 +418,7 @@ static Opcode unary(Parser* parser, ASTNode** nodeHandle) {
|
||||
//temp handle to potentially negate values
|
||||
parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal
|
||||
|
||||
//check for negative literals (optimisation)
|
||||
//optimisation: check for negative literals
|
||||
if (tmpNode->type == AST_NODE_LITERAL && (IS_INTEGER(tmpNode->atomic.literal) || IS_FLOAT(tmpNode->atomic.literal))) {
|
||||
//negate directly, if int or float
|
||||
Literal lit = tmpNode->atomic.literal;
|
||||
@@ -888,7 +888,7 @@ ParseRule* getRule(TokenType type) {
|
||||
return &parseRules[type];
|
||||
}
|
||||
|
||||
//constant folding (optimisation)
|
||||
//optimisation: constant folding
|
||||
static bool calcStaticBinaryArithmetic(Parser* parser, ASTNode** nodeHandle) {
|
||||
switch((*nodeHandle)->binary.opcode) {
|
||||
case OP_ADDITION:
|
||||
@@ -1321,37 +1321,6 @@ static void importStmt(Parser* parser, ASTNode** nodeHandle) {
|
||||
freeLiteral(alias);
|
||||
}
|
||||
|
||||
static void exportStmt(Parser* parser, ASTNode** nodeHandle) {
|
||||
//read the identifier
|
||||
ASTNode* node = NULL;
|
||||
advance(parser);
|
||||
identifier(parser, &node);
|
||||
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Literal idn = copyLiteral(node->atomic.literal);
|
||||
freeASTNode(node);
|
||||
|
||||
Literal alias = TO_NULL_LITERAL;
|
||||
|
||||
if (match(parser, TOKEN_AS)) {
|
||||
ASTNode* node;
|
||||
advance(parser);
|
||||
identifier(parser, &node);
|
||||
alias = copyLiteral(node->atomic.literal);
|
||||
freeASTNode(node);
|
||||
}
|
||||
|
||||
emitASTNodeExport(nodeHandle, idn, alias);
|
||||
|
||||
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of export statement");
|
||||
|
||||
freeLiteral(idn);
|
||||
freeLiteral(alias);
|
||||
}
|
||||
|
||||
//precedence functions
|
||||
static void expressionStmt(Parser* parser, ASTNode** nodeHandle) {
|
||||
//BUGFIX: check for empty statements
|
||||
@@ -1431,12 +1400,6 @@ static void statement(Parser* parser, ASTNode** nodeHandle) {
|
||||
return;
|
||||
}
|
||||
|
||||
//export
|
||||
if (match(parser, TOKEN_EXPORT)) {
|
||||
exportStmt(parser, nodeHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//default
|
||||
expressionStmt(parser, nodeHandle);
|
||||
}
|
||||
|
||||
+3
-6
@@ -57,17 +57,14 @@ int lengthRefString(RefString* refString) {
|
||||
}
|
||||
|
||||
RefString* copyRefString(RefString* refString) {
|
||||
//Cheaty McCheater Face
|
||||
refString->refcount++;
|
||||
return refString;
|
||||
}
|
||||
|
||||
RefString* deepCopyRefString(RefString* refString) {
|
||||
//deep copy, which can be modified immediately
|
||||
RefString* newRefString = (RefString*)allocate(NULL, 0, sizeof(int) * 2 + refString->length + 1);
|
||||
|
||||
memcpy(newRefString, refString, refString->length + 1);
|
||||
|
||||
return newRefString;
|
||||
//create a new string, with a new refcount
|
||||
return createRefStringLength(refString->data, refString->length);
|
||||
}
|
||||
|
||||
char* toCString(RefString* refString) {
|
||||
|
||||
+19
-2
@@ -70,8 +70,14 @@ static bool checkType(Literal typeLiteral, Literal original, Literal value, bool
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new entry
|
||||
//if null, assume it's a new array variable that needs checking
|
||||
if (IS_NULL(original)) {
|
||||
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
|
||||
if (!checkType( ((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], TO_NULL_LITERAL, AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -97,8 +103,19 @@ static bool checkType(Literal typeLiteral, Literal original, Literal value, bool
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new entry to a parent
|
||||
//if null, assume it's a new dictionary variable that needs checking
|
||||
if (IS_NULL(original)) {
|
||||
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
|
||||
//check the type of key and value
|
||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], TO_NULL_LITERAL, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], TO_NULL_LITERAL, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+3
-9
@@ -5,25 +5,19 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 6
|
||||
#define TOY_VERSION_PATCH 3
|
||||
#define TOY_VERSION_MINOR 7
|
||||
#define TOY_VERSION_PATCH 0
|
||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||
|
||||
//platform exports/imports
|
||||
//platform-specific specifications
|
||||
#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
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
//test basic import/export
|
||||
{
|
||||
var variable: int = 42;
|
||||
export variable as field;
|
||||
}
|
||||
|
||||
{
|
||||
import field as value;
|
||||
assert value == 42, "import/export failed";
|
||||
}
|
||||
|
||||
|
||||
//test functions using import/export
|
||||
{
|
||||
fn f() {
|
||||
import field;
|
||||
|
||||
assert field == 42, "import in function failed";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//test importing/exporting of functions
|
||||
{
|
||||
fn func() {
|
||||
return 69;
|
||||
}
|
||||
|
||||
export func;
|
||||
}
|
||||
|
||||
{
|
||||
import func;
|
||||
assert func() == 69, "import/export of functions failed";
|
||||
}
|
||||
|
||||
|
||||
//test that variables retain their types with the typeof keyword
|
||||
{
|
||||
var t: type = int;
|
||||
|
||||
export t;
|
||||
}
|
||||
|
||||
{
|
||||
import t;
|
||||
|
||||
assert typeof t == type, "type retention failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
var t = astype [int];
|
||||
var arr: t = [1, 2, 3.14];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
var t = astype [string:int];
|
||||
var dict: t = ["one": 1, "two": 2, 3:4];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
var t = astype [string:int];
|
||||
var dict: t = ["one": 1, "two": 2, "pi": 3.14];
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -1,11 +0,0 @@
|
||||
//test exports
|
||||
var field: int = 42;
|
||||
|
||||
fn function() {
|
||||
return 69;
|
||||
}
|
||||
|
||||
export field;
|
||||
export function;
|
||||
|
||||
print "All good";
|
||||
@@ -1,10 +0,0 @@
|
||||
//test imports
|
||||
import field;
|
||||
//import function;
|
||||
|
||||
//assert field == 42, "import field failed";
|
||||
|
||||
//assert function() == 69, "import function failed";
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -186,7 +186,6 @@ int main() {
|
||||
"dot-chaining.toy",
|
||||
"dottify-bugfix.toy",
|
||||
"functions.toy",
|
||||
"imports-and-exports.toy",
|
||||
"index-arrays.toy",
|
||||
"index-dictionaries.toy",
|
||||
"index-strings.toy",
|
||||
@@ -212,38 +211,6 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//read source
|
||||
size_t dummy;
|
||||
size_t exportSize, importSize;
|
||||
char* exportSource = readFile("scripts/separate-exports.toy", &dummy);
|
||||
char* importSource = readFile("scripts/separate-imports.toy", &dummy);
|
||||
|
||||
//compile
|
||||
unsigned char* exportBinary = compileString(exportSource, &exportSize);
|
||||
unsigned char* importBinary = compileString(importSource, &importSize);
|
||||
|
||||
//run the interpreter over both binaries
|
||||
Interpreter interpreter;
|
||||
initInterpreter(&interpreter);
|
||||
|
||||
//NOTE: supress print output for testing
|
||||
setInterpreterPrint(&interpreter, noPrintFn);
|
||||
setInterpreterAssert(&interpreter, noAssertFn);
|
||||
|
||||
runInterpreter(&interpreter, exportBinary, exportSize); //automatically frees the binary data
|
||||
|
||||
resetInterpreter(&interpreter);
|
||||
|
||||
runInterpreter(&interpreter, importBinary, importSize); //automatically frees the binary data
|
||||
|
||||
freeInterpreter(&interpreter);
|
||||
|
||||
//cleanup
|
||||
free((void*)exportSource);
|
||||
free((void*)importSource);
|
||||
}
|
||||
|
||||
//1, to allow for the assertion test
|
||||
if (ignoredAssertions > 1) {
|
||||
fprintf(stderr, ERROR "Assertions hidden: %d\n", ignoredAssertions);
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "interpreter.h"
|
||||
|
||||
#include "console_colors.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//suppress the print output
|
||||
static void noPrintFn(const char* output) {
|
||||
//NO OP
|
||||
}
|
||||
|
||||
int errorsTriggered = 0;
|
||||
static void noErrorFn(const char* output) {
|
||||
errorsTriggered++;
|
||||
}
|
||||
|
||||
//compilation functions
|
||||
char* readFile(char* path, size_t* fileSize) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
fseek(file, 0L, SEEK_END);
|
||||
*fileSize = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* buffer = (char*)malloc(*fileSize + 1);
|
||||
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||
|
||||
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||
|
||||
if (bytesRead < *fileSize) {
|
||||
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char* compileString(char* source, size_t* size) {
|
||||
Lexer lexer;
|
||||
Parser parser;
|
||||
Compiler compiler;
|
||||
|
||||
initLexer(&lexer, source);
|
||||
initParser(&parser, &lexer);
|
||||
initCompiler(&compiler);
|
||||
|
||||
//run the parser until the end of the source
|
||||
ASTNode* node = scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//pack up and leave
|
||||
if (node->type == AST_NODE_ERROR) {
|
||||
printf(ERROR "error node detected\n" RESET);
|
||||
freeASTNode(node);
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
writeCompiler(&compiler, node);
|
||||
freeASTNode(node);
|
||||
node = scanParser(&parser);
|
||||
}
|
||||
|
||||
//get the bytecode dump
|
||||
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
|
||||
|
||||
//cleanup
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
//no lexer to clean up
|
||||
|
||||
//finally
|
||||
return tb;
|
||||
}
|
||||
|
||||
void runBinary(unsigned char* tb, size_t size) {
|
||||
Interpreter interpreter;
|
||||
initInterpreter(&interpreter);
|
||||
|
||||
//NOTE: suppress print output for testing
|
||||
setInterpreterPrint(&interpreter, noPrintFn);
|
||||
setInterpreterError(&interpreter, noErrorFn);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int main() {
|
||||
bool success = true;
|
||||
|
||||
{
|
||||
//run each file in tests/scripts/
|
||||
char* filenames[] = {
|
||||
"declare-types-array.toy",
|
||||
"declare-types-dictionary-key.toy",
|
||||
"declare-types-dictionary-value.toy",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; filenames[i]; i++) {
|
||||
printf("Running (must fail) %s\n", filenames[i]);
|
||||
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "scripts/mustfail/%s", filenames[i]);
|
||||
|
||||
runSourceFile(buffer);
|
||||
|
||||
if (errorsTriggered == 0) {
|
||||
printf(ERROR "Expected error did not occur in %s\n" RESET, filenames[i]);
|
||||
success = false;
|
||||
}
|
||||
|
||||
errorsTriggered = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf(NOTICE "All good\n" RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user