Compare commits

...

29 Commits

Author SHA1 Message Date
Kayne Ruse f6ec6a8c73 The any type is now recognized as a type properly 2023-06-19 23:16:46 +10:00
Kayne Ruse 2157b2f540 Fixed an obscure compiler bug involving assignments and indexing, read more
TOY_OP_INDEX_ASSIGN_INTERMEDIATE was being used when it shouldn't have.

Now the check runs down the whole binary->right branch to ensure the given
node doesn't exist in that tree.
2023-06-15 12:29:25 +10:00
Kayne Ruse 1481216e69 Fixed chained functions, resolved #52 2023-06-14 17:41:30 +10:00
Kayne Ruse f25f389b4e Removed a macro that potentially broke the build
Gonna have to live with repl code in the lib for now.
2023-06-14 16:53:48 +10:00
Kayne Ruse deff784df8 Removed a speed test script 2023-06-14 16:40:01 +10:00
Kayne Ruse 54e82846c3 Massive dict copying optimisation, read more
I simply pre-allocated the new dict to the right size. This skips
internal copying logic which was repeated on every expansion. This
Should increase scope copying as well.

I applied the same logic to arrays, but the increase in speed was tiny.
2023-06-13 14:49:46 +10:00
Kayne Ruse 67fce427eb Added an initial sorted test to the sort() function 2023-06-13 08:17:42 +10:00
Kayne Ruse 8a2cb61435 Made quicksort on mostly-sorted arrays more efficient 2023-06-13 07:28:54 +10:00
Ratstail91 50d03e28fc Fixed MSVC compilation 2023-06-12 00:05:24 +10:00
Kayne Ruse 763581c73b Added header-only parsing to the repl, read more
Also:

* Ensured TOY_VERSION_BUILD is consistent throughout the whole build
* Updated README.md
2023-06-07 23:55:30 +10:00
Kayne Ruse cdb2613e5d Disallowed fn decl in for loop pre clause 2023-06-07 19:20:50 +10:00
Kayne Ruse 733df87c08 Added dist target, lowered recursion depth limit 2023-06-07 14:58:51 +10:00
Kayne Ruse bfd506f497 Forgot memory allocator for reffunctions 2023-06-07 02:02:35 +10:00
Kayne Ruse 18b59c9e84 Bumped version number 2023-06-07 00:11:34 +10:00
Ratstail91 d3eb31d964 Added TOY_DISABLE_REPL option for compiling 2023-06-07 00:04:05 +10:00
Kayne Ruse 07f4a98b95 Replacing Toy_Literal function bytecode with Toy_RefFunction, addressing #77
This seems to have worked way too easily.
2023-06-06 23:35:59 +10:00
Kayne Ruse 0949fd6ff9 Dang 2023-06-06 21:46:42 +10:00
Kayne Ruse 03e5096f10 Moved test_sum into it's own directory under scripts/ 2023-06-06 21:14:05 +10:00
Kayne Ruse bb81b8c474 Changed recursion limit to 10,000 (was 200) 2023-06-06 21:02:01 +10:00
Kayne Ruse cf6db57787 Whitespace tweak 2023-03-25 01:43:51 +11:00
Kayne Ruse 17f0e4476b Caught a bug that the test cases failed to find 2023-03-17 21:58:13 +11:00
Kayne Ruse 1095e1a885 Added type casting a grouping bugfix, resolved #76 2023-03-17 20:57:47 +11:00
Kayne Ruse 2edfbbe3ef Found a compiler bug, thanks Aedan! 2023-03-17 14:01:16 +11:00
Ratstail91 4b83f1f0d6 Fixed a dumb typo 2023-03-15 06:39:19 +11:00
Kayne Ruse e2fa1cf2e8 Moved lib_runner's drive system into the core of the lang 2023-03-15 06:12:35 +11:00
Kayne Ruse a04d2c4816 Tweaked TOY_EXPORT omitting extra repl stuff 2023-03-15 04:56:26 +11:00
Kayne Ruse f2f8aed23a Added short-circuiting support to && and || 2023-03-11 17:59:09 +11:00
Kayne Ruse 68ed52b347 Tweaked precedence of binary expressions 2023-03-11 17:47:43 +11:00
Kayne Ruse 88dac53ae0 Added toy.h, thanks for the suggestion GabrielGavrilov!
Resolved #72
2023-03-10 08:41:58 +11:00
34 changed files with 764 additions and 307 deletions
+6 -3
View File
@@ -4,9 +4,11 @@
# Toy
This is the Toy programming language interpreter, written in C.
The Toy programming language is an imperative bytecode-intermediate embedded scripting language. It isn't intended to operate on its own, but rather as part of another program, the "host". This process is intended to allow a decent amount of easy customisation by the host's end user, by exposing logic in script files. Alternatively, binary files in a custom format can be used as well.
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
The host will provide all of the extensions needed on a case-by-case basis. Script files have the `.toy` file extension, while binary files have the `.tb` file extension.
This is the Toy programming language interpreter, written in C.
# Nifty Features
@@ -14,7 +16,7 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
* Bytecode intermediate compilation
* Optional, but robust type system (including `opaque` for arbitrary data)
* Functions and types are first-class citizens
* Import external libraries
* Import native libraries from the host
* Fancy slice notation for strings, arrays and dictionaries
* Can re-direct output, error and assertion failure messages
* Open source under the zlib license
@@ -71,3 +73,4 @@ This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
* Seth A. Robinson
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
+2 -2
View File
@@ -115,13 +115,13 @@
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>LIB_RUNNER_EXPORT</PreprocessorDefinitions>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>LIB_RUNNER_EXPORT</PreprocessorDefinitions>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
+5
View File
@@ -123,6 +123,7 @@
<ClCompile Include="source\toy_builtin.c" />
<ClCompile Include="source\toy_common.c" />
<ClCompile Include="source\toy_compiler.c" />
<ClCompile Include="source\toy_drive_system.c" />
<ClCompile Include="source\toy_interpreter.c" />
<ClCompile Include="source\toy_keyword_types.c" />
<ClCompile Include="source\toy_lexer.c" />
@@ -131,15 +132,18 @@
<ClCompile Include="source\toy_literal_dictionary.c" />
<ClCompile Include="source\toy_memory.c" />
<ClCompile Include="source\toy_parser.c" />
<ClCompile Include="source\toy_reffunction.c" />
<ClCompile Include="source\toy_refstring.c" />
<ClCompile Include="source\toy_scope.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\toy.h" />
<ClInclude Include="source\toy_ast_node.h" />
<ClInclude Include="source\toy_builtin.h" />
<ClInclude Include="source\toy_common.h" />
<ClInclude Include="source\toy_compiler.h" />
<ClInclude Include="source\toy_console_colors.h" />
<ClInclude Include="source\toy_drive_system.h" />
<ClInclude Include="source\toy_interpreter.h" />
<ClInclude Include="source\toy_keyword_types.h" />
<ClInclude Include="source\toy_lexer.h" />
@@ -149,6 +153,7 @@
<ClInclude Include="source\toy_memory.h" />
<ClInclude Include="source\toy_opcodes.h" />
<ClInclude Include="source\toy_parser.h" />
<ClInclude Include="source\toy_reffunction.h" />
<ClInclude Include="source\toy_refstring.h" />
<ClInclude Include="source\toy_scope.h" />
<ClInclude Include="source\toy_token_types.h" />
+6 -6
View File
@@ -1,7 +1,3 @@
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
# export CFLAGS+=-fsanitize=address,undefined
export CFLAGS+=-std=c18 -pedantic -Werror
export TOY_OUTDIR = out
@@ -28,12 +24,16 @@ library: $(TOY_OUTDIR)
static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static
library-release: $(TOY_OUTDIR)
library-release: clean $(TOY_OUTDIR)
$(MAKE) -j8 -C source library-release
static-release: $(TOY_OUTDIR)
static-release: clean $(TOY_OUTDIR)
$(MAKE) -j8 -C source static-release
#distribution
dist: export CFLAGS+=-O2 -mtune=native -march=native
dist: repl-release
#utils
test: clean $(TOY_OUTDIR)
$(MAKE) -C test
+10 -145
View File
@@ -1,13 +1,12 @@
#include "lib_runner.h"
#include "toy_memory.h"
#include "toy_drive_system.h"
#include "toy_interpreter.h"
#include "repl_tools.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Toy_Runner {
Toy_Interpreter interpreter;
@@ -38,7 +37,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
return -1;
}
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral);
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
@@ -113,70 +112,19 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
return -1;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n");
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
//check for file extensions
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) {
interpreter->errorOutput("Bad binary file extension (expected .tb)\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
//check for break-out attempts
for (size_t i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
}
//use raw types - easier
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
//load the bytecode
size_t fileSize = 0;
@@ -203,7 +151,8 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
TOY_FREE_ARRAY(char, filePath, realLength);
//free the drive path
Toy_freeLiteral(filePathLiteral);
return 1;
}
@@ -602,87 +551,3 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
return 0;
}
//file system API
static Toy_LiteralDictionary Toy_driveDictionary;
void Toy_initDriveDictionary() {
Toy_initLiteralDictionary(&Toy_driveDictionary);
}
void Toy_freeDriveDictionary() {
Toy_freeLiteralDictionary(&Toy_driveDictionary);
}
Toy_LiteralDictionary* Toy_getDriveDictionary() {
return &Toy_driveDictionary;
}
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
//check for break-out attempts
for (size_t i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return result;
}
-27
View File
@@ -1,35 +1,8 @@
#pragma once
#include "toy_common.h"
#include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
#define TOY_OPAQUE_TAG_RUNNER 100
//platform/compiler-specific instructions - because MSVC + Box Engine are dumber than a bag of rocks
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
#define LIB_RUNNER_API extern
#elif defined(_MSC_VER)
#ifndef LIB_RUNNER_EXPORT
#define LIB_RUNNER_API __declspec(dllimport)
#else
#define LIB_RUNNER_API __declspec(dllexport)
#endif
#else
#define LIB_RUNNER_API extern
#endif
//file system API - these need to be set by the host
LIB_RUNNER_API void Toy_initDriveDictionary();
LIB_RUNNER_API void Toy_freeDriveDictionary();
LIB_RUNNER_API Toy_LiteralDictionary* Toy_getDriveDictionary();
//file system API - for use with other libs
LIB_RUNNER_API Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+34 -3
View File
@@ -1471,8 +1471,13 @@ static void recursiveLiteralQuicksortUtil(Toy_Interpreter* interpreter, Toy_Lite
swapLiteralsUtil(&ptr[runner], &ptr[literalCount - 1]);
//recurse on each end
recursiveLiteralQuicksortUtil(interpreter, &ptr[0], runner, fnCompareLiteral);
recursiveLiteralQuicksortUtil(interpreter, &ptr[runner + 1], literalCount - runner - 1, fnCompareLiteral);
if (runner > 0) {
recursiveLiteralQuicksortUtil(interpreter, &ptr[0], runner, fnCompareLiteral);
}
if (runner < literalCount) {
recursiveLiteralQuicksortUtil(interpreter, &ptr[runner + 1], literalCount - runner - 1, fnCompareLiteral);
}
}
static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
@@ -1511,8 +1516,34 @@ static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
return -1;
}
//BUGFIX: check if the array is already sorted
bool sorted = true;
for (int checker = 0; checker < TOY_AS_ARRAY(selfLiteral)->count - 1 && sorted; checker++) {
Toy_LiteralArray arguments;
Toy_LiteralArray returns;
Toy_initLiteralArray(&arguments);
Toy_initLiteralArray(&returns);
Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[checker]);
Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[checker + 1]);
Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns);
Toy_Literal lessThan = Toy_popLiteralArray(&returns);
Toy_freeLiteralArray(&arguments);
Toy_freeLiteralArray(&returns);
if (!TOY_IS_TRUTHY(lessThan)) {
sorted = false;
}
Toy_freeLiteral(lessThan);
}
//call the quicksort util
if (TOY_IS_ARRAY(selfLiteral)) {
if (!sorted) {
recursiveLiteralQuicksortUtil(interpreter, TOY_AS_ARRAY(selfLiteral)->literals, TOY_AS_ARRAY(selfLiteral)->count, fnLiteral);
}
+16 -20
View File
@@ -6,10 +6,7 @@
#include "toy_console_colors.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
#include "toy.h"
#include <stdio.h>
#include <stdlib.h>
@@ -106,16 +103,9 @@ void repl(const char* initialInput) {
int main(int argc, const char* argv[]) {
Toy_initCommandLine(argc, argv);
//lib setup (hacky - only really for this program)
Toy_initDriveDictionary();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//setup the drive system (for filesystem access)
Toy_initDriveSystem();
Toy_setDrivePath("scripts", "scripts");
//command line specific actions
if (Toy_commandLine.error) {
@@ -151,7 +141,7 @@ int main(int argc, const char* argv[]) {
Toy_runSourceFile(Toy_commandLine.sourcefile);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
@@ -161,7 +151,7 @@ int main(int argc, const char* argv[]) {
Toy_runSource(Toy_commandLine.source);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
@@ -203,11 +193,17 @@ int main(int argc, const char* argv[]) {
return -1;
}
//run the binary file
Toy_runBinaryFile(Toy_commandLine.binaryfile);
if (Toy_commandLine.parseBytecodeHeader) {
//only parse the bytecode header
Toy_parseBinaryFileHeader(Toy_commandLine.binaryfile);
}
else {
//run the binary file
Toy_runBinaryFile(Toy_commandLine.binaryfile);
}
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
@@ -228,7 +224,7 @@ int main(int argc, const char* argv[]) {
repl(initialSource);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
+59 -3
View File
@@ -78,10 +78,10 @@ const unsigned char* Toy_compileString(const char* source, size_t* size) {
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//run the parser until the end of the source
//step 1 - run the parser until the end of the source
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and leave
//on error, pack up and leave
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
@@ -94,7 +94,7 @@ const unsigned char* Toy_compileString(const char* source, size_t* size) {
node = Toy_scanParser(&parser);
}
//get the bytecode dump
//step 2 - get the bytecode dump
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
//cleanup
@@ -149,3 +149,59 @@ void Toy_runSourceFile(const char* fname) {
Toy_runSource(source);
free((void*)source);
}
//utils for debugging the header
static unsigned char readByte(const unsigned char* tb, int* count) {
unsigned char ret = *(unsigned char*)(tb + *count);
*count += 1;
return ret;
}
static const char* readString(const unsigned char* tb, int* count) {
const unsigned char* ret = tb + *count;
*count += strlen((char*)ret) + 1; //+1 for null character
return (const char*)ret;
}
void Toy_parseBinaryFileHeader(const char* fname) {
size_t size = 0; //not used
const unsigned char* tb = Toy_readFile(fname, &size);
if (!tb || size < 4) {
return;
}
int count = 0;
//header section
const unsigned char major = readByte(tb, &count);
const unsigned char minor = readByte(tb, &count);
const unsigned char patch = readByte(tb, &count);
const char* build = readString(tb, &count);
printf("Toy Programming Language Interpreter Version %d.%d.%d (interpreter built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
printf("Toy Programming Language Bytecode Version ");
//print the output
if (major == TOY_VERSION_MAJOR && minor == TOY_VERSION_MINOR && patch == TOY_VERSION_PATCH) {
printf("%d.%d.%d", major, minor, patch);
}
else {
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%d.%d.%d" TOY_CC_RESET, major, minor, patch);
}
printf(" (interpreter built on ");
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD)) == 0) {
printf("%s", build);
}
else {
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%s" TOY_CC_RESET, build);
}
printf(")\n");
//cleanup
free((void*)tb);
}
+1
View File
@@ -12,3 +12,4 @@ void Toy_runBinaryFile(const char* fname);
void Toy_runSource(const char* source);
void Toy_runSourceFile(const char* fname);
void Toy_parseBinaryFileHeader(const char* fname);
+1 -1
View File
@@ -4,7 +4,7 @@ fn fib(n : int) {
return fib(n-1) + fib(n-2);
}
for (var i = 0; i < 20; i++) {
for (var i = 0; i <= 35; i++) {
var res = fib(i);
print string i + ": " + string res;
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
/* toy.h - A Toy Programming Language
If you're looking how to use Toy directly, try https://toylang.com/
Otherwise, these headers may help learn how Toy works internally.
*/
/* utilities - these define a bunch of useful macros based on platform.
The most important one is `TOY_API`, which highlights functions intended for the end user.
*/
#include "toy_common.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_drive_system.h"
/* core pipeline - from source to execution
Each step is as follows:
source -> lexer -> token
token -> parser -> AST
AST -> compiler -> bytecode
bytecode -> interpreter -> result
I should note that the parser -> compiler phase is actually made up of two steps - the write step
and the collate step. See `Toy_compileString()` in `repl/repl_tools.c` for an example of how to compile
properly.
*/
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
/* building block structures - the basic units of operation
Literals represent any value within the language, including some internal ones that you never see.
Literal Arrays are literally arrays within memory, and are the most heavily used structure in Toy.
Literal Dictionaries are unordered key-value hashmaps, that use a running strategy for collisions.
*/
#include "toy_literal.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
/* other components - you probably won't use these directly, but they're a good learning opportunity.
`Toy_Scope` holds the variables of a specific scope within Toy - be it a script, a function, a block, etc.
Scopes are also where the type system lives at runtime. They use identifier literals as keys, exclusively.
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and
faster to copy and move. In reality, since strings are considered immutable, multiple variables can point
to the same string to save memory, and you can just create a new one of these vars pointing to the original
rather than copying entirely for a speed boost. This module has it's own memory allocator system that is
plugged into the main memory allocator.
`Toy_RefFunction` acts similarly to `Toy_RefString`, but instead operates on function bytecode.
*/
#include "toy_scope.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
+19 -1
View File
@@ -4,7 +4,7 @@
#include <string.h>
#include <assert.h>
//test variable sizes based on platform
//test variable sizes based on platform - see issue #35
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(char) == 1);
@@ -15,6 +15,12 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
STATIC_ASSERT(sizeof(unsigned short) == 2);
STATIC_ASSERT(sizeof(unsigned int) == 4);
static const char* build = __DATE__ " " __TIME__;
const char* Toy_private_version_build() {
return build;
}
//declare the singleton with default values
Toy_CommandLine Toy_commandLine = {
.error = false,
@@ -27,6 +33,7 @@ Toy_CommandLine Toy_commandLine = {
.source = NULL,
.initialfile = NULL,
.enablePrintNewline = true,
.parseBytecodeHeader = false,
.verbose = false
};
@@ -87,6 +94,16 @@ void Toy_initCommandLine(int argc, const char* argv[]) {
continue;
}
if (!strcmp(argv[i], "-p")) {
Toy_commandLine.parseBytecodeHeader = true;
if (Toy_commandLine.binaryfile) {
Toy_commandLine.error = false;
}
continue;
}
if (!strcmp(argv[i], "-n")) {
Toy_commandLine.enablePrintNewline = false;
Toy_commandLine.error = false;
@@ -122,6 +139,7 @@ void Toy_helpCommandLine(int argc, const char* argv[]) {
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
}
+6 -3
View File
@@ -6,8 +6,8 @@
#define TOY_VERSION_MAJOR 1
#define TOY_VERSION_MINOR 1
#define TOY_VERSION_PATCH 1
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
#define TOY_VERSION_PATCH 5
#define TOY_VERSION_BUILD Toy_private_version_build()
//platform/compiler-specific instructions
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
@@ -28,7 +28,9 @@
#endif
//for processing the command line arguments
TOY_API const char* Toy_private_version_build();
//for processing the command line arguments in the repl
typedef struct {
bool error;
bool help;
@@ -40,6 +42,7 @@ typedef struct {
char* source;
char* initialfile;
bool enablePrintNewline;
bool parseBytecodeHeader;
bool verbose;
} Toy_CommandLine;
+110 -7
View File
@@ -259,6 +259,81 @@ static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
return index;
}
//BUGFIX: check to see if this node lies within this tree
bool checkNodeInTree(Toy_ASTNode* tree, Toy_ASTNode* node) {
if (tree == node) {
return true;
}
if (tree == NULL) {
return false;
}
switch(tree->type) {
case TOY_AST_NODE_UNARY:
return checkNodeInTree(tree->unary.child, node);
case TOY_AST_NODE_BINARY:
return checkNodeInTree(tree->binary.left, node) || checkNodeInTree(tree->binary.right, node);
case TOY_AST_NODE_TERNARY:
return checkNodeInTree(tree->ternary.condition, node) || checkNodeInTree(tree->ternary.thenPath, node) || checkNodeInTree(tree->ternary.elsePath, node);
case TOY_AST_NODE_GROUPING:
return checkNodeInTree(tree->grouping.child, node);
case TOY_AST_NODE_BLOCK:
return checkNodeInTree(tree->block.nodes, node);
case TOY_AST_NODE_COMPOUND:
return checkNodeInTree(tree->compound.nodes, node);
case TOY_AST_NODE_PAIR:
return checkNodeInTree(tree->pair.left, node) || checkNodeInTree(tree->pair.right, node);
case TOY_AST_NODE_INDEX:
return checkNodeInTree(tree->index.first, node) || checkNodeInTree(tree->index.second, node) || checkNodeInTree(tree->index.third, node);
case TOY_AST_NODE_VAR_DECL:
return checkNodeInTree(tree->varDecl.expression, node);
case TOY_AST_NODE_FN_COLLECTION:
return checkNodeInTree(tree->fnCollection.nodes, node);
case TOY_AST_NODE_FN_DECL:
return checkNodeInTree(tree->fnDecl.arguments, node) || checkNodeInTree(tree->fnDecl.returns, node) || checkNodeInTree(tree->fnDecl.block, node);
case TOY_AST_NODE_FN_CALL:
return checkNodeInTree(tree->fnCall.arguments, node);
case TOY_AST_NODE_FN_RETURN:
return checkNodeInTree(tree->returns.returns, node);
case TOY_AST_NODE_IF:
return checkNodeInTree(tree->pathIf.condition, node) || checkNodeInTree(tree->pathIf.thenPath, node) || checkNodeInTree(tree->pathIf.elsePath, node);
case TOY_AST_NODE_WHILE:
return checkNodeInTree(tree->pathWhile.condition, node) || checkNodeInTree(tree->pathWhile.thenPath, node);
case TOY_AST_NODE_FOR:
return checkNodeInTree(tree->pathFor.preClause, node) || checkNodeInTree(tree->pathFor.condition, node) || checkNodeInTree(tree->pathFor.postClause, node) || checkNodeInTree(tree->pathFor.thenPath, node);
case TOY_AST_NODE_ERROR:
case TOY_AST_NODE_LITERAL:
case TOY_AST_NODE_BREAK:
case TOY_AST_NODE_CONTINUE:
case TOY_AST_NODE_PREFIX_INCREMENT:
case TOY_AST_NODE_PREFIX_DECREMENT:
case TOY_AST_NODE_POSTFIX_INCREMENT:
case TOY_AST_NODE_POSTFIX_DECREMENT:
case TOY_AST_NODE_IMPORT:
case TOY_AST_NODE_PASS:
return false;
}
return false;
}
//NOTE: jumpOfsets are included, because function arg and return indexes are embedded in the code body i.e. need to include their sizes in the jump
//NOTE: rootNode should NOT include groupings and blocks
static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode* node, void* breakAddressesPtr, void* continueAddressesPtr, int jumpOffsets, Toy_ASTNode* rootNode) {
@@ -322,7 +397,8 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
//return this if...
Toy_Opcode ret = Toy_writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN) && rootNode->binary.right != node) { //range-based check for assignment type; make sure the index is on the left of the assignment symbol
//range-based check for assignment type; make sure the index is on the left of the assignment symbol
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN) && !checkNodeInTree(rootNode->binary.right, node)) {
return TOY_OP_INDEX_ASSIGN_INTERMEDIATE;
}
@@ -331,9 +407,37 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
return node->binary.opcode;
}
if (ret != TOY_OP_EOF && (node->binary.opcode == TOY_OP_VAR_ASSIGN || node->binary.opcode == TOY_OP_AND || node->binary.opcode == TOY_OP_OR || (node->binary.opcode >= TOY_OP_COMPARE_EQUAL && node->binary.opcode <= TOY_OP_INVERT))) {
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
ret = TOY_OP_EOF; //untangle in this case
//untangle in these cases - (WTF, are you serious?)
if (ret != TOY_OP_EOF) {
switch(node->binary.opcode) {
case TOY_OP_NEGATE:
case TOY_OP_ADDITION:
case TOY_OP_SUBTRACTION:
case TOY_OP_MULTIPLICATION:
case TOY_OP_DIVISION:
case TOY_OP_MODULO:
case TOY_OP_VAR_ASSIGN:
case TOY_OP_VAR_ADDITION_ASSIGN:
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
case TOY_OP_VAR_DIVISION_ASSIGN:
case TOY_OP_VAR_MODULO_ASSIGN:
case TOY_OP_COMPARE_EQUAL:
case TOY_OP_COMPARE_NOT_EQUAL:
case TOY_OP_COMPARE_LESS:
case TOY_OP_COMPARE_LESS_EQUAL:
case TOY_OP_COMPARE_GREATER:
case TOY_OP_COMPARE_GREATER_EQUAL:
case TOY_OP_INVERT:
case TOY_OP_AND:
case TOY_OP_OR:
//place the rhs result before the outer instruction
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
ret = TOY_OP_EOF;
default:
break;
}
}
compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
@@ -490,8 +594,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
//create the function in the literal cache (by storing the compiler object)
Toy_Literal fnLiteral = TOY_TO_FUNCTION_LITERAL(fnCompiler, 0);
fnLiteral.type = TOY_LITERAL_FUNCTION_INTERMEDIATE; //NOTE: changing type
Toy_Literal fnLiteral = ((Toy_Literal){ .as = { .generic = fnCompiler }, .type = TOY_LITERAL_FUNCTION_INTERMEDIATE});
//push the name
int identifierIndex = Toy_findLiteralIndex(&compiler->literalCache, node->fnDecl.identifier);
@@ -1182,7 +1285,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, size_t* s
case TOY_LITERAL_FUNCTION_INTERMEDIATE: {
//extract the compiler
Toy_Literal fn = compiler->literalCache.literals[i];
void* fnCompiler = TOY_AS_FUNCTION(fn).inner.bytecode; //store the compiler here for now
void* fnCompiler = fn.as.generic; //store the compiler here for now
//collate the function into bytecode (without header)
size_t size = 0;
+11 -1
View File
@@ -1,6 +1,16 @@
#pragma once
//NOTE: you need both font AND background for these to work
/* toy_console_colors.h - console utility
This file provides a number of macros that can set the color of text in a console
window. These are used for convenience only. They are supposed to be dropped into
a printf()'s first argument, like so:
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
NOTE: you need both font AND background for these to work
*/
//platform/compiler-specific instructions
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
+99
View File
@@ -0,0 +1,99 @@
#include "toy_drive_system.h"
#include "toy_memory.h"
#include "toy_literal_dictionary.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//file system API
static Toy_LiteralDictionary driveDictionary;
void Toy_initDriveSystem() {
Toy_initLiteralDictionary(&driveDictionary);
}
void Toy_freeDriveSystem() {
Toy_freeLiteralDictionary(&driveDictionary);
}
TOY_API void Toy_setDrivePath(char* drive, char* path) {
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
}
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
if (!TOY_IS_STRING(pathLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
Toy_deleteRefString(filePath);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
//clean up the drive/path stuff
Toy_deleteRefString(drivePath);
Toy_deleteRefString(filePath);
Toy_deleteRefString(path);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//check for break-out attempts
for (size_t i = 0; i < fileLength - 1; i++) {
if (file[i] == '.' && file[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, file, fileLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
TOY_FREE_ARRAY(char, file, fileLength + 1);
return result;
}
+12
View File
@@ -0,0 +1,12 @@
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_interpreter.h"
//file system API - these need to be set by the host
TOY_API void Toy_initDriveSystem();
TOY_API void Toy_freeDriveSystem();
//file system API - for use with libs
TOY_API void Toy_setDrivePath(char* drive, char* path);
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+22 -24
View File
@@ -12,12 +12,17 @@
#include <string.h>
static void printWrapper(const char* output) {
//allow for disabling of newlines in the repl
#ifndef TOY_EXPORT
if (Toy_commandLine.enablePrintNewline) {
printf("%s\n", output);
}
else {
printf("%s", output);
}
#else
printf("%s\n", output);
#endif
}
static void assertWrapper(const char* output) {
@@ -1082,11 +1087,12 @@ static bool execAnd(Toy_Interpreter* interpreter) {
return false;
}
if (TOY_IS_TRUTHY(lhs) && TOY_IS_TRUTHY(rhs)) {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(true));
//short-circuit support
if (!TOY_IS_TRUTHY(lhs)) {
Toy_pushLiteralArray(&interpreter->stack, lhs);
}
else {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(false));
Toy_pushLiteralArray(&interpreter->stack, rhs);
}
Toy_freeLiteral(lhs);
@@ -1115,11 +1121,12 @@ static bool execOr(Toy_Interpreter* interpreter) {
return false;
}
if (TOY_IS_TRUTHY(lhs) || TOY_IS_TRUTHY(rhs)) {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(true));
//short-circuit support
if (TOY_IS_TRUTHY(lhs)) {
Toy_pushLiteralArray(&interpreter->stack, lhs);
}
else {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(false));
Toy_pushLiteralArray(&interpreter->stack, rhs);
}
Toy_freeLiteral(lhs);
@@ -1186,7 +1193,7 @@ static void readInterpreterSections(Toy_Interpreter* interpreter);
//also supports identifier & arg1 to be other way around (looseFirstArgument)
static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) {
//BUGFIX: depth check - don't drown!
if (interpreter->depth >= 200) {
if (interpreter->depth >= 1000) {
interpreter->errorOutput("Infinite recursion detected - panicking\n");
interpreter->panic = true;
return false;
@@ -1222,12 +1229,8 @@ static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) {
//get the function literal
Toy_Literal func = identifier;
if (!Toy_parseIdentifierToValue(interpreter, &func)) {
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(stackSize);
if (!TOY_IS_FUNCTION(func) && Toy_parseIdentifierToValue(interpreter, &func)) {
Toy_freeLiteral(identifier);
return false;
}
if (!TOY_IS_FUNCTION(func) && !TOY_IS_FUNCTION_NATIVE(func)) {
@@ -1264,7 +1267,6 @@ static bool execFnCall(Toy_Interpreter* interpreter, bool looseFirstArgument) {
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(func);
Toy_freeLiteral(stackSize);
Toy_freeLiteral(identifier);
return ret;
}
@@ -1316,8 +1318,8 @@ bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_Liter
//init the inner interpreter manually
Toy_initLiteralArray(&inner.literalCache);
inner.scope = Toy_pushScope(func.as.function.scope);
inner.bytecode = TOY_AS_FUNCTION(func).inner.bytecode;
inner.length = TOY_AS_FUNCTION_BYTECODE_LENGTH(func);
inner.bytecode = ((Toy_RefFunction*)(TOY_AS_FUNCTION(func).inner.ptr))->data;
inner.length = ((Toy_RefFunction*)(TOY_AS_FUNCTION(func).inner.ptr))->length;
inner.count = 0;
inner.codeStart = -1;
inner.depth = interpreter->depth + 1;
@@ -2421,20 +2423,16 @@ static void readInterpreterSections(Toy_Interpreter* interpreter) {
//get the size of the function
size_t size = (size_t)readShort(interpreter->bytecode, &interpreter->count);
//read the function code (literal cache and all)
unsigned char* bytes = TOY_ALLOCATE(unsigned char, size);
memcpy(bytes, interpreter->bytecode + interpreter->count, size); //TODO: -1 for the ending mark
interpreter->count += size;
//assert that the last memory slot is function end
if (bytes[size - 1] != TOY_OP_FN_END) {
if (interpreter->bytecode[interpreter->count + size - 1] != TOY_OP_FN_END) {
interpreter->errorOutput("[internal] Failed to find function end");
TOY_FREE_ARRAY(unsigned char, bytes, size);
return;
}
//change the type to normal
interpreter->literalCache.literals[i] = TOY_TO_FUNCTION_LITERAL(bytes, size);
//copies internally, since functions can exist independant of literalCache
interpreter->literalCache.literals[i] = TOY_TO_FUNCTION_LITERAL(Toy_createRefFunction(interpreter->bytecode + interpreter->count, size));
interpreter->count += size;
}
}
+24 -15
View File
@@ -59,7 +59,7 @@ void Toy_freeLiteral(Toy_Literal literal) {
if (TOY_IS_FUNCTION(literal)) {
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
TOY_AS_FUNCTION(literal).scope = NULL;
TOY_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(literal));
Toy_deleteRefFunction((Toy_RefFunction*)(TOY_AS_FUNCTION(literal).inner.ptr));
}
if (TOY_IS_TYPE(literal) && TOY_AS_TYPE(literal).capacity > 0) {
@@ -84,12 +84,8 @@ bool Toy_private_isTruthy(Toy_Literal x) {
return true;
}
Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){{ .string = { .ptr = ptr }},TOY_LITERAL_STRING, 0});
}
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){{ .identifier = { .ptr = ptr, .hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }},TOY_LITERAL_IDENTIFIER, 0});
return ((Toy_Literal){{ .identifier = { .ptr = ptr, .hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }},TOY_LITERAL_IDENTIFIER});
}
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
@@ -123,6 +119,10 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
//preallocate enough space
array->capacity = TOY_AS_ARRAY(original)->capacity;
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, 0, array->capacity);
//copy each element
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
@@ -135,6 +135,15 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//preallocate enough space
dictionary->capacity = TOY_AS_DICTIONARY(original)->capacity;
dictionary->entries = TOY_ALLOCATE(Toy_private_dictionary_entry, dictionary->capacity);
for (int i = 0; i < dictionary->capacity; i++) {
dictionary->entries[i].key = TOY_TO_NULL_LITERAL;
dictionary->entries[i].value = TOY_TO_NULL_LITERAL;
}
//copy each entry
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
@@ -146,10 +155,8 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
}
case TOY_LITERAL_FUNCTION: {
unsigned char* buffer = TOY_ALLOCATE(unsigned char, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
memcpy(buffer, TOY_AS_FUNCTION(original).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(Toy_copyRefFunction( TOY_AS_FUNCTION(original).inner.ptr ));
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(buffer, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
return literal;
@@ -174,7 +181,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return original; //literally a shallow copy
}
case TOY_LITERAL_ARRAY_INTERMEDIATE: {
case TOY_LITERAL_ARRAY_INTERMEDIATE: { //TODO: efficient preallocation?
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
@@ -190,7 +197,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return ret;
}
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: { //TODO: efficient preallocation?
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
@@ -206,7 +213,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return ret;
}
case TOY_LITERAL_TYPE_INTERMEDIATE: {
case TOY_LITERAL_TYPE_INTERMEDIATE: { //TODO: efficient preallocation?
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
@@ -378,8 +385,10 @@ int Toy_hashLiteral(Toy_Literal lit) {
case TOY_LITERAL_INTEGER:
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
case TOY_LITERAL_FLOAT:
return hashUInt(*(unsigned int*)(&TOY_AS_FLOAT(lit)));
case TOY_LITERAL_FLOAT: {
unsigned int* ptr = (unsigned int*)(&TOY_AS_FLOAT(lit));
return hashUInt(*ptr);
}
case TOY_LITERAL_STRING:
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
@@ -446,7 +455,7 @@ static void printToBuffer(const char* str) {
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str ? str : "\0");
globalPrintCount += strlen(str);
}
+22 -20
View File
@@ -3,6 +3,7 @@
#include "toy_common.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
//forward delcare stuff
struct Toy_Literal;
@@ -55,7 +56,7 @@ typedef struct Toy_Literal {
struct {
union {
void* bytecode; //8
Toy_RefFunction* ptr; //8
Toy_NativeFn native; //8
Toy_HookFn hook; //8
} inner; //8
@@ -63,12 +64,11 @@ typedef struct Toy_Literal {
} function; //16
struct { //for variable names
Toy_RefString* ptr; //8
Toy_RefString* ptr; //8
int hash; //4
} identifier; //16
} identifier; //16
struct {
struct Toy_Literal* subtypes; //8
Toy_LiteralType typeOf; //4
unsigned char capacity; //1
@@ -80,10 +80,13 @@ typedef struct Toy_Literal {
void* ptr; //8
int tag; //4
} opaque; //16
void* generic; //8
} as; //16
Toy_LiteralType type; //4
int bytecodeLength; //4 - shenanigans with byte alignment reduces the size of Toy_Literal
//4 - unused
//shenanigans with byte alignment reduces the size of Toy_Literal
} Toy_Literal;
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
@@ -113,29 +116,29 @@ typedef struct Toy_Literal {
#define TOY_AS_TYPE(value) ((value).as.type)
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL, 0})
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN, 0})
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER, 0})
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT, 0})
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY, 0})
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY, 0})
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){{ .function = { .inner = { .bytecode = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION, l})
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE, 0})
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK, 0})
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL})
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN})
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER})
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT})
#define TOY_TO_STRING_LITERAL(value) ((Toy_Literal){{ .string = { .ptr = value }},TOY_LITERAL_STRING})
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY})
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY})
#define TOY_TO_FUNCTION_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .ptr = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION})
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE})
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK})
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE, 0})
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE, 0})
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE})
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE})
//BUGFIX: For blank indexing
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK, 0})
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
TOY_API void Toy_freeLiteral(Toy_Literal literal);
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) ((lit).bytecodeLength)
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) (Toy_lengthRefFunction((lit).inner.ptr))
#define TOY_MAX_STRING_LENGTH 4096
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
@@ -144,7 +147,6 @@ TOY_API void Toy_freeLiteral(Toy_Literal literal);
//BUGFIX: macros are not functions
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
TOY_API Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
+2
View File
@@ -1,5 +1,6 @@
#include "toy_memory.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
#include "toy_console_colors.h"
@@ -49,4 +50,5 @@ void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
allocator = fn;
Toy_setRefStringAllocatorFn(fn);
Toy_setRefFunctionAllocatorFn(fn);
}
+53 -17
View File
@@ -119,6 +119,7 @@ ParseRule parseRules[];
static void declaration(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, PrecedenceRule rule);
static Toy_Literal readTypeToLiteral(Toy_Parser* parser);
static void varDecl(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
//TODO: resolve the messy order of these
//the expression rules
@@ -367,73 +368,73 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//assignment
case TOY_TOKEN_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_ASSIGN;
}
case TOY_TOKEN_PLUS_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_ADDITION_ASSIGN;
}
case TOY_TOKEN_MINUS_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_SUBTRACTION_ASSIGN;
}
case TOY_TOKEN_MULTIPLY_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_MULTIPLICATION_ASSIGN;
}
case TOY_TOKEN_DIVIDE_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_DIVISION_ASSIGN;
}
case TOY_TOKEN_MODULO_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_MODULO_ASSIGN;
}
//comparison
case TOY_TOKEN_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_EQUAL;
}
case TOY_TOKEN_NOT_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_NOT_EQUAL;
}
case TOY_TOKEN_LESS: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_LESS;
}
case TOY_TOKEN_LESS_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_LESS_EQUAL;
}
case TOY_TOKEN_GREATER: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_GREATER;
}
case TOY_TOKEN_GREATER_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_GREATER_EQUAL;
}
case TOY_TOKEN_AND: {
parsePrecedence(parser, nodeHandle, PREC_AND);
parsePrecedence(parser, nodeHandle, PREC_AND + 1);
return TOY_OP_AND;
}
case TOY_TOKEN_OR: {
parsePrecedence(parser, nodeHandle, PREC_OR);
parsePrecedence(parser, nodeHandle, PREC_OR + 1);
return TOY_OP_OR;
}
@@ -627,6 +628,14 @@ static Toy_Opcode castingPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
}
break;
//BUGFIX: handle this here, and not in castingPrefix, so "any" can be recognized as a type properly
case TOY_TOKEN_ANY: {
Toy_Literal literal = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, false);
Toy_emitASTNodeLiteral(nodeHandle, literal);
Toy_freeLiteral(literal);
}
break;
default:
error(parser, parser->previous, "Unexpected token passed to casting precedence rule");
return TOY_OP_EOF;
@@ -658,7 +667,7 @@ static Toy_Opcode castingInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
break;
case TOY_TOKEN_LITERAL_STRING:
atomic(parser, nodeHandle);
string(parser, nodeHandle);
break;
default:
@@ -739,6 +748,27 @@ static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
}
static Toy_Opcode fnCall(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//wait - is the previous token a type? this should be casting instead
if (parser->previous.type >= TOY_TOKEN_NULL && parser->previous.type <= TOY_TOKEN_ANY) {
//casting type
Toy_ASTNode* lhsNode = NULL;
castingPrefix(parser, &lhsNode);
advance(parser);
//casting value
Toy_ASTNode* rhsNode = NULL;
grouping(parser, &rhsNode);
//emit the cast node
Toy_emitASTNodeBinary(&lhsNode, rhsNode, TOY_OP_TYPE_CAST);
//pass it off to the caller
*nodeHandle = lhsNode;
return TOY_OP_GROUPING_BEGIN; //dummy value
}
advance(parser); //skip the left paren
//binary() is an infix rule - so only get the RHS of the operator
@@ -918,7 +948,7 @@ ParseRule parseRules[] = { //must match the token types
{NULL, NULL, PREC_NONE},// TOKEN_DICTIONARY,
{NULL, NULL, PREC_NONE},// TOKEN_FUNCTION,
{NULL, NULL, PREC_NONE},// TOKEN_OPAQUE,
{NULL, NULL, PREC_NONE},// TOKEN_ANY,
{castingPrefix, NULL, PREC_CALL},// TOKEN_ANY,
//keywords and reserved words
{NULL, NULL, PREC_NONE},// TOKEN_AS,
@@ -1376,7 +1406,13 @@ static void forStmt(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//check the pre-clause
if (parser->current.type != TOY_TOKEN_SEMICOLON) {
declaration(parser, &preClause); //allow defining variables in the pre-clause
//allow defining variables in the pre-clause
if (match(parser, TOY_TOKEN_VAR)) {
varDecl(parser, &preClause);
}
else {
parsePrecedence(parser, &preClause, PREC_ASSIGNMENT);
}
}
else {
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after empty declaration of for clause");
+55
View File
@@ -0,0 +1,55 @@
#include "toy_reffunction.h"
#include <string.h>
//memory allocation
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
static Toy_RefFunctionAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn allocator) {
allocate = allocator;
}
//API
Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length) {
//allocate the memory area (including metadata space)
Toy_RefFunction* refFunction = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * length);
if (refFunction == NULL) {
return NULL;
}
//set the data
refFunction->refCount = 1;
refFunction->length = length;
memcpy(refFunction->data, data, refFunction->length);
return refFunction;
}
void Toy_deleteRefFunction(Toy_RefFunction* refFunction) {
//decrement, then check
refFunction->refCount--;
if (refFunction->refCount <= 0) {
allocate(refFunction, sizeof(size_t) + sizeof(int) + sizeof(char) * (refFunction->length + 1), 0);
}
}
int Toy_countRefFunction(Toy_RefFunction* refFunction) {
return refFunction->refCount;
}
size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction) {
return refFunction->length;
}
Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction) {
//Cheaty McCheater Face
refFunction->refCount++;
return refFunction;
}
Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction) {
//create a new function, with a new refCount
return Toy_createRefFunction(refFunction->data, refFunction->length);
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "toy_common.h"
//memory allocation hook
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
//the RefFunction structure
typedef struct Toy_RefFunction {
size_t length;
int refCount;
unsigned char data[];
} Toy_RefFunction;
//API
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
@@ -0,0 +1,7 @@
var s = "42";
var t = "69";
assert int (s + t) - 1 == 4268, "casting parentheses failed";
print "All good";
+3
View File
@@ -23,5 +23,8 @@ assert !false, "!false";
var c = false;
assert !c, "!c";
//test multiple comparisons
assert 1 == 2 == false, "Left-accociative equality failed";
print "All good";
+23
View File
@@ -0,0 +1,23 @@
var days = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday"
];
var rng = 10; //for chosen at random
var index = rng % days.length();
assert index == 3, "dot modulo bugfix failed";
rng %= days.length();
assert rng == 3, "dot modulo assign bugfix failed";
print "All good";
@@ -0,0 +1,30 @@
{
fn a() {
fn b() {
return 42;
}
return b;
}
assert a()() == 42, "function within function failed";
}
{
fn a() {
fn b() {
fn c() {
return 42;
}
return c;
}
return b;
}
assert a()()() == 42, "function within function within function failed";
}
print "All good";
@@ -0,0 +1,10 @@
var result; //result must exist to ensure assingment, rather than declaration is invoked by the comparison below
var lhs = [0];
var rhs = [0];
result = lhs[0] < rhs[0]; //make sure this doesn't invoke TOY_OP_INDEX_ASSIGN_INTERMEDIATE
print "All good";
@@ -0,0 +1,9 @@
//explicitly support && and || short circuits
assert 1 && 2 == 2, "&& short-circuit failed";
assert 1 || 2 == 1, "|| short-circuit failed";
print "All good";
+5
View File
@@ -22,4 +22,9 @@ var dict: complex = [
"third array": [7, 8, 9]
];
//check the any type is recognized as a type within an array
var a: [type] = [int, bool, any];
print "All good";
+5
View File
@@ -108,16 +108,20 @@ int main() {
//run each file in tests/scripts/
const char* filenames[] = {
"arithmetic.toy",
"casting-parentheses-bugfix.toy",
"casting.toy",
"coercions.toy",
"comparisons.toy",
"dot-and-matrix.toy",
"dot-assignments-bugfix.toy",
"dot-chaining.toy",
"dot-modulo-bugfix.toy",
"dottify-bugfix.toy",
"function-within-function-bugfix.toy",
"functions.toy",
"index-arrays.toy",
"index-assignment-both-bugfix.toy",
"index-assignment-intermediate-bugfix.toy",
"index-assignment-left-bugfix.toy",
"index-dictionaries.toy",
"index-strings.toy",
@@ -132,6 +136,7 @@ int main() {
"panic-within-functions.toy",
"polyfill-insert.toy",
"polyfill-remove.toy",
"short-circuiting-support.toy",
"ternary-expressions.toy",
"types.toy",
NULL
+4 -9
View File
@@ -6,6 +6,7 @@
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_drive_system.h"
#include <stdio.h>
#include <stdlib.h>
@@ -63,15 +64,9 @@ typedef struct Payload {
int main() {
//setup the runner filesystem (hacky)
Toy_initDriveDictionary();
Toy_initDriveSystem();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
Toy_setDrivePath("scripts", "scripts");
{
//run each file in test/scripts
@@ -113,7 +108,7 @@ int main() {
}
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
if (!failedAsserts) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);