Compare commits

..

60 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
Kayne Ruse f84cdff883 Fixed order of operations 2023-03-07 06:49:17 +11:00
Ratstail91 f869c9425a Corrected an error message 2023-03-05 13:05:16 +11:00
Ratstail91 76ddd5703e Hack: just track the intermediate depth externally 2023-03-05 00:24:07 +11:00
Ratstail91 669808730e Minor tweak that shouldn't break anything 2023-03-04 22:57:41 +11:00
Ratstail91 e6d9809da5 Famous last words: I think I fixed it 2023-03-04 22:18:17 +11:00
Ratstail91 502032e514 Testing an obscure bugfix 2023-03-04 15:41:55 +11:00
Ratstail91 6e9d42f892 Merge branch 'dev' 2023-02-28 17:39:05 +11:00
Ratstail91 70ca27486e Bugfix a leak? 2023-02-28 17:37:43 +11:00
Ratstail91 12fa434e0f Experimenting with cleaning up loopy code 2023-02-28 17:29:37 +11:00
Ratstail91 efc1e764d2 Patched a casting error in round 2023-02-27 23:27:11 +11:00
Kayne Ruse c5c0122243 BUGFIX: typeof keyword precedence was off 2023-02-27 21:47:38 +11:00
Kayne Ruse 348b7b8c24 Added some math utils to standard
* ceil
* floor
* max
* min
* round
2023-02-27 21:32:31 +11:00
Kayne Ruse e243ad949a Removed a divide instruction (modulo) from the final output, thanks Wren! 2023-02-26 22:41:58 +11:00
Ratstail91 9b673f23ad Reduced C callstack size in Toy_Scope 2023-02-26 22:31:37 +11:00
Kayne Ruse 624a0c80ba Prevented NO-OP calls to the memory allocator
Also shaved off about 1-2 milliseconds of execution time of fib-memo.toy
2023-02-26 21:20:22 +11:00
Ratstail91 1064b69d04 BUGFIX: Integer and float comparisons always return true 2023-02-26 01:27:21 +11:00
Ratstail91 e9b347acb6 MSVC + Box Engine are dumber than a bag of rocks 2023-02-25 04:40:12 +11:00
Ratstail91 071c8da2aa Visual Studio broke itself - fixed 2023-02-25 04:28:07 +11:00
Ratstail91 d6538812bf Merge branch 'main' of https://github.com/Ratstail91/Toy 2023-02-25 04:18:03 +11:00
Ratstail91 3aeddff736 Tweaks to dictionary for performance 2023-02-24 22:13:50 +11:00
Ratstail91 c88c1b125d Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-24 21:53:42 +11:00
Kayne Ruse 1513ba9878 tweaked scripts folder 2023-02-23 22:45:38 +11:00
Kayne Ruse bc0289c3f4 tweaked scripts folder 2023-02-23 20:23:10 +11:00
Kayne Ruse 92c71a374d Implemented a basic random library 2023-02-23 19:19:17 +11:00
Kayne Ruse e0547474b8 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-23 18:37:11 +11:00
Kayne Ruse 3e6d21afbb Added abs(), hash() to libstandard 2023-02-23 18:36:12 +11:00
Kayne Ruse d3df01c1c4 Updated .gitignore 2023-02-23 03:33:52 +11:00
Ratstail91 cdca6fa45c Fixed directory in solution file 2023-02-22 20:06:48 +11:00
Kayne Ruse 1dde9d8f29 Improved error message in set() and push()
The actual issue was that the type check wasn't catching the issue, so
it reached the scope before it was caught. Fixed it, anyway.
2023-02-20 13:04:35 +00:00
Kayne Ruse 7f0f17b6e0 Patched up failures from Toy_parseIdentifierToValue
I really don't like that function - it needs to be replaced.
2023-02-20 06:11:30 +00:00
Kayne Ruse 3507104121 Fixed indexAccess potentially going awry with bad inputs
There's always one or two that slip through
2023-02-20 05:28:25 +00:00
54 changed files with 2210 additions and 838 deletions
+2
View File
@@ -19,6 +19,8 @@ bin/
*.db
*.o
*.a
*.so
*.dll
*.exe
*.meta
*.log
+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.
+11 -6
View File
@@ -113,18 +113,16 @@
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
</Link>
<ClCompile>
<AdditionalIncludeDirectories>C:\Users\kayne\Desktop\Toy\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>
</PreprocessorDefinitions>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>
</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Users\kayne\Desktop\Toy\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
@@ -133,6 +131,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="repl\lib_about.c" />
<ClCompile Include="repl\lib_random.c" />
<ClCompile Include="repl\lib_runner.c" />
<ClCompile Include="repl\lib_standard.c" />
<ClCompile Include="repl\repl_main.c" />
@@ -140,10 +139,16 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="repl\lib_about.h" />
<ClInclude Include="repl\lib_random.h" />
<ClInclude Include="repl\lib_runner.h" />
<ClInclude Include="repl\lib_standard.h" />
<ClInclude Include="repl\repl_tools.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="Toy.vcxproj">
<Project>{26360002-cc2a-469a-9b28-ba0c1af41657}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
+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
+196
View File
@@ -0,0 +1,196 @@
#include "lib_random.h"
#include "toy_memory.h"
static int hashInt(int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
typedef struct Toy_RandomGenerator {
int seed; //mutated with each call
} Toy_RandomGenerator;
//Toy native functions
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
return -1;
}
//get the seed argument
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
Toy_Literal seedLiteralIdn = seedLiteral;
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
Toy_freeLiteral(seedLiteralIdn);
}
if (TOY_IS_IDENTIFIER(seedLiteral)) {
Toy_freeLiteral(seedLiteral);
return -1;
}
if (!TOY_IS_INTEGER(seedLiteral)) {
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
Toy_freeLiteral(seedLiteral);
return -1;
}
//generate the generator object
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
generator->seed = TOY_AS_INTEGER(seedLiteral);
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
//return and cleanup
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
Toy_freeLiteral(seedLiteral);
Toy_freeLiteral(generatorLiteral);
return 1;
}
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
return -1;
}
//get the runner object
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
Toy_Literal generatorLiteralIdn = generatorLiteral;
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
Toy_freeLiteral(generatorLiteralIdn);
}
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
Toy_freeLiteral(generatorLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
return -1;
}
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
//generate the new value and package up the return
generator->seed = hashInt(generator->seed);
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
Toy_freeLiteral(generatorLiteral);
Toy_freeLiteral(resultLiteral);
return 0;
}
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
return -1;
}
//get the runner object
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
Toy_Literal generatorLiteralIdn = generatorLiteral;
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
Toy_freeLiteral(generatorLiteralIdn);
}
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
Toy_freeLiteral(generatorLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
return -1;
}
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
//clear out the runner object
TOY_FREE(Toy_RandomGenerator, generator);
Toy_freeLiteral(generatorLiteral);
return 0;
}
//call the hook
typedef struct Natives {
const char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"createRandomGenerator", nativeCreateRandomGenerator},
{"generateRandomNumber", nativeGenerateRandomNumber},
{"freeRandomGenerator", nativeFreeRandomGenerator},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
#define TOY_OPAQUE_TAG_RANDOM 200
+51 -144
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;
@@ -33,7 +32,12 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
Toy_freeLiteral(drivePathLiteralIdn);
}
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
Toy_freeLiteral(drivePathLiteral);
return -1;
}
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
@@ -103,70 +107,24 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
Toy_freeLiteral(drivePathLiteralIdn);
}
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 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_IDENTIFIER(drivePathLiteral)) {
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);
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
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);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(drivePathLiteral);
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;
}
}
Toy_freeLiteral(drivePathLiteral);
//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;
@@ -193,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;
}
@@ -213,6 +172,11 @@ static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in runScript\n");
return -1;
@@ -260,6 +224,12 @@ static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in getScriptVar\n");
return -1;
@@ -332,6 +302,12 @@ static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in callScriptFn\n");
return -1;
@@ -401,6 +377,11 @@ static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arg
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in resetScript\n");
return -1;
@@ -437,6 +418,11 @@ static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in freeScript\n");
return -1;
@@ -471,6 +457,11 @@ static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in checkScriptDirty\n");
return -1;
@@ -560,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;
}
-8
View File
@@ -1,16 +1,8 @@
#pragma once
#include "toy_common.h"
#include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
//file system API - these need to be set by the host
TOY_API void Toy_initDriveDictionary();
TOY_API void Toy_freeDriveDictionary();
TOY_API Toy_LiteralDictionary* Toy_getDriveDictionary();
#define TOY_OPAQUE_TAG_RUNNER 100
//file system API - for use with other libs
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+492 -6
View File
@@ -32,6 +32,339 @@ static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
return 1;
}
static int nativeHash(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to hash\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result = TOY_TO_INTEGER_LITERAL(Toy_hashLiteral(selfLiteral));
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeAbs(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to abs\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to abs\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(selfLiteral) > 0 ? TOY_AS_INTEGER(selfLiteral) : -TOY_AS_INTEGER(selfLiteral) );
}
if (TOY_IS_FLOAT(selfLiteral)) {
result = TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(selfLiteral) > 0 ? TOY_AS_FLOAT(selfLiteral) : -TOY_AS_FLOAT(selfLiteral) );
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeCeil(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to ceil\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to ceil\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
//NO-OP
result = Toy_copyLiteral(selfLiteral);
}
if (TOY_IS_FLOAT(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL( (int)TOY_AS_FLOAT(selfLiteral) - TOY_AS_FLOAT(selfLiteral) == 0 ? (int)TOY_AS_FLOAT(selfLiteral) : (int)TOY_AS_FLOAT(selfLiteral) + 1 );
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeFloor(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to floor\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to floor\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
//NO-OP
result = Toy_copyLiteral(selfLiteral);
}
if (TOY_IS_FLOAT(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL( (int)TOY_AS_FLOAT(selfLiteral) );
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeMax(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//return value
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
//iterate over all arguments
do {
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to max\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
//if not comparing yet...
if (TOY_IS_NULL(resultLiteral)) {
resultLiteral = selfLiteral;
continue;
}
//cooerce if needed
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
resultLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(resultLiteral) );
}
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
selfLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(selfLiteral) );
}
//compare
if (TOY_IS_INTEGER(resultLiteral) && TOY_AS_INTEGER(resultLiteral) < TOY_AS_INTEGER(selfLiteral)) {
//NOTE: just ints, don't free
resultLiteral = selfLiteral;
}
else if (TOY_IS_FLOAT(resultLiteral) && TOY_AS_FLOAT(resultLiteral) < TOY_AS_FLOAT(selfLiteral)) {
//NOTE: just floats, don't free
resultLiteral = selfLiteral;
}
}
while (arguments->count > 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeMin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//return value
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
//iterate over all arguments
do {
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to min\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
//if not comparing yet...
if (TOY_IS_NULL(resultLiteral)) {
resultLiteral = selfLiteral;
continue;
}
//cooerce if needed
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
resultLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(resultLiteral) );
}
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
selfLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(selfLiteral) );
}
//compare
if (TOY_IS_INTEGER(resultLiteral) && TOY_AS_INTEGER(resultLiteral) > TOY_AS_INTEGER(selfLiteral)) {
//NOTE: just ints, don't free
resultLiteral = selfLiteral;
}
else if (TOY_IS_FLOAT(resultLiteral) && TOY_AS_FLOAT(resultLiteral) > TOY_AS_FLOAT(selfLiteral)) {
//NOTE: just floats, don't free
resultLiteral = selfLiteral;
}
}
while (arguments->count > 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeRound(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to round\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to round\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
//NO-OP
result = Toy_copyLiteral(selfLiteral);
}
if (TOY_IS_FLOAT(selfLiteral)) {
//catch the already-rounded case
if (TOY_AS_FLOAT(selfLiteral) == (int)TOY_AS_FLOAT(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL((int)TOY_AS_FLOAT(selfLiteral));
}
else {
result = TOY_TO_INTEGER_LITERAL( TOY_AS_FLOAT(selfLiteral) - (int)TOY_AS_FLOAT(selfLiteral) < 0.5 ? (int)TOY_AS_FLOAT(selfLiteral) : (int)TOY_AS_FLOAT(selfLiteral) + 1 );
}
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
@@ -54,6 +387,12 @@ static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
Toy_freeLiteral(otherLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(otherLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(otherLiteral);
return -1;
}
//for each self type
if (TOY_IS_ARRAY(selfLiteral)) {
if (!TOY_IS_ARRAY(otherLiteral)) {
@@ -164,6 +503,12 @@ static int nativeContainsKey(Toy_Interpreter* interpreter, Toy_LiteralArray* arg
Toy_freeLiteral(keyLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(keyLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(keyLiteral);
return -1;
}
//check type
if (!(/* TOY_IS_ARRAY(selfLiteral) || */ TOY_IS_DICTIONARY(selfLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to containsKey\n");
@@ -209,6 +554,12 @@ static int nativeContainsValue(Toy_Interpreter* interpreter, Toy_LiteralArray* a
Toy_freeLiteral(valueLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(valueLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to containsValue\n");
@@ -279,6 +630,12 @@ static int nativeEvery(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to every\n");
@@ -395,6 +752,12 @@ static int nativeFilter(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to filter\n");
@@ -509,6 +872,12 @@ static int nativeForEach(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to forEach\n");
@@ -581,6 +950,11 @@ static int nativeGetKeys(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//check type
if (!TOY_IS_DICTIONARY(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to getKeys\n");
@@ -626,6 +1000,11 @@ static int nativeGetValues(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//check type
if (!TOY_IS_DICTIONARY(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to getValues\n");
@@ -678,6 +1057,12 @@ static int nativeIndexOf(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(valueLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(valueLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
//check type
if (!TOY_IS_ARRAY(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to indexOf\n");
@@ -725,6 +1110,12 @@ static int nativeMap(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to map\n");
@@ -834,6 +1225,13 @@ static int nativeReduce(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(defaultLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(defaultLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to reduce\n");
@@ -929,6 +1327,12 @@ static int nativeSome(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to some\n");
@@ -1067,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) {
@@ -1093,6 +1502,12 @@ static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!TOY_IS_ARRAY(selfLiteral) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to sort\n");
@@ -1101,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);
}
@@ -1129,6 +1570,11 @@ static int nativeToLower(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to toLower\n");
Toy_freeLiteral(selfLiteral);
@@ -1190,6 +1636,11 @@ static int nativeToString(Toy_Interpreter* interpreter, Toy_LiteralArray* argume
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//BUGFIX: probably an undefined variable
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
@@ -1229,6 +1680,11 @@ static int nativeToUpper(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to toUpper\n");
Toy_freeLiteral(selfLiteral);
@@ -1289,7 +1745,13 @@ static int nativeTrim(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(selfLiteralIdn);
}
if (!TOY_IS_STRING(selfLiteral)) {
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(trimCharsLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(trimCharsLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to trim\n");
Toy_freeLiteral(trimCharsLiteral);
Toy_freeLiteral(selfLiteral);
@@ -1400,7 +1862,13 @@ static int nativeTrimBegin(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
Toy_freeLiteral(selfLiteralIdn);
}
if (!TOY_IS_STRING(selfLiteral)) {
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(trimCharsLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(trimCharsLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to trimBegin\n");
Toy_freeLiteral(trimCharsLiteral);
Toy_freeLiteral(selfLiteral);
@@ -1488,7 +1956,13 @@ static int nativeTrimEnd(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (!TOY_IS_STRING(selfLiteral)) {
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(trimCharsLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(trimCharsLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to trimEnd\n");
Toy_freeLiteral(trimCharsLiteral);
Toy_freeLiteral(selfLiteral);
@@ -1557,7 +2031,19 @@ typedef struct Natives {
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
//misc. utils
{"clock", nativeClock},
{"hash", nativeHash},
//math utils
{"abs", nativeAbs},
{"ceil", nativeCeil},
{"floor", nativeFloor},
{"max", nativeMax},
{"min", nativeMin},
{"round", nativeRound},
//compound utils
{"concat", nativeConcat}, //array, dictionary, string
{"containsKey", nativeContainsKey}, //dictionary
{"containsValue", nativeContainsValue}, //array, dictionary
+18 -20
View File
@@ -1,14 +1,12 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_standard.h"
#include "lib_random.h"
#include "lib_runner.h"
#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>
@@ -29,6 +27,7 @@ void repl(const char* initialInput) {
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
for(;;) {
@@ -104,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) {
@@ -149,7 +141,7 @@ int main(int argc, const char* argv[]) {
Toy_runSourceFile(Toy_commandLine.sourcefile);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
@@ -159,7 +151,7 @@ int main(int argc, const char* argv[]) {
Toy_runSource(Toy_commandLine.source);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
@@ -201,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;
}
@@ -226,7 +224,7 @@ int main(int argc, const char* argv[]) {
repl(initialSource);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
+61 -3
View File
@@ -1,6 +1,7 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_standard.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "toy_console_colors.h"
@@ -77,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);
@@ -93,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
@@ -112,6 +113,7 @@ void Toy_runBinary(const unsigned char* tb, size_t size) {
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
Toy_runInterpreter(&interpreter, tb, (int)size);
@@ -147,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);
-125
View File
@@ -1,125 +0,0 @@
import node;
//constants
var SPEED: int const = 10;
//variables
var parent: opaque = null;
var posX: int = 50;
var posY: int = 50;
var WIDTH: int const = 100;
var HEIGHT: int const = 100;
var xspeed: int = 0;
var yspeed: int = 0;
//accessors - variables are private, functions are public
fn getX(node: opaque) {
return posX;
}
fn getY(node: opaque) {
return posY;
}
//lifecycle functions
fn onInit(node: opaque) {
print "render.toy:onInit() called\n";
node.loadTexture("sprites:/character.png");
parent = node.getNodeParent();
}
fn onStep(node: opaque) {
posX += xspeed;
posY += yspeed;
}
fn onFree(node: opaque) {
print "render.toy:onFree() called\n";
node.freeTexture();
}
fn onDraw(node: opaque) {
// print "render.toy:onDraw() called\n";
var px = parent.callNode("getX");
var py = parent.callNode("getY");
if (px == null) {
px = 0;
}
if (py == null) {
py = 0;
}
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
}
//event functions
fn onKeyDown(node: opaque, event: string) {
if (event == "character_up") {
yspeed -= SPEED;
return;
}
if (event == "character_down") {
yspeed += SPEED;
return;
}
if (event == "character_left") {
xspeed -= SPEED;
return;
}
if (event == "character_right") {
xspeed += SPEED;
return;
}
}
fn onKeyUp(node: opaque, event: string) {
if (event == "character_up" && yspeed < 0) {
yspeed = 0;
return;
}
if (event == "character_down" && yspeed > 0) {
yspeed = 0;
return;
}
if (event == "character_left" && xspeed < 0) {
xspeed = 0;
return;
}
if (event == "character_right" && xspeed > 0) {
xspeed = 0;
return;
}
}
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
// print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")\n";
}
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")\n";
//jump to pos
posX = x - WIDTH / 2;
posY = y - HEIGHT / 2;
}
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")\n";
}
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
// print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")\n";
}
-89
View File
@@ -1,89 +0,0 @@
//single line comment
/*
multi line comment
*/
//test primitive literals
print "hello world";
print null;
print true;
print false;
print 42;
print 3.14;
print -69;
print -4.20;
print 2 + (3 * 3);
//test operators (integers)
print 1 + 1;
print 1 - 1;
print 2 * 2;
print 1 / 2;
print 4 % 2;
//test operators (floats)
print 1.0 + 1.0;
print 1.0 - 1.0;
print 2.0 * 2.0;
print 1.0 / 2.0;
//test scopes
{
print "This statement is within a scope.";
{
print "This is a deeper scope.";
}
}
print "Back to the outer scope.";
//test scope will delegate to higher scope
var a = 1;
{
a = 2;
print a;
}
print a;
//test scope will shadow higher scope on redefine
var b: int = 3;
{
var b = 4;
print b;
}
print b;
//test compounds, repeatedly
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
//test empties
print [];
print [:];
//test nested compounds
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
//var declarations
var x = 31;
var y : int = 42;
var arr : [int] = [1, 2, 3, 42];
var dict : [string:int] = ["hello": 1, "world":2];
//printing expressions
print x;
print x + y;
print arr;
print dict;
//test asserts at the end of the file
assert x, "This won't be seen";
assert true, "This won't be seen";
assert false, "This is a failed assert, and will end execution";
print "This will not be printed because of the above assert";
+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;
}
+3 -3
View File
@@ -34,8 +34,8 @@ var tiles: [[int]] const = [
];
var tileset: [int: string] const = [
0: " ",
1: " X "
0: " ",
1: "X "
];
//variables
@@ -48,7 +48,7 @@ fn draw() {
for (var i: int = 0; i < WIDTH; i++) {
//draw the player pos
if (i == posX && j == posY) {
print " O ";
print "O ";
continue;
}
+36
View File
@@ -0,0 +1,36 @@
/*
Since this is a pseudo-random generator, and there's no internal state to the algorithm other
than the generator opaque, there needs to be a "call counter" (current depth) to shuffle the
initial seeds, otherwise generators created from other generators will resemble their parents,
but one call greater.
*/
import standard;
import random;
var DEPTH: int const = 20;
var levels = [];
//generate the level seeds
var generator: opaque = createRandomGenerator(clock().hash());
for (var i: int = 0; i < DEPTH; i++) {
levels.push(generator.generateRandomNumber());
}
generator.freeRandomGenerator();
//generate "levels" of a roguelike
for (var i = 0; i < DEPTH; i++) {
var rng: opaque = createRandomGenerator(levels[i] + i);
print "---";
print levels[i];
print rng.generateRandomNumber();
print rng.generateRandomNumber();
print rng.generateRandomNumber();
rng.freeRandomGenerator();
}
View File
+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"
+15 -9
View File
@@ -40,17 +40,21 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
break;
case TOY_AST_NODE_BLOCK:
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
if (node->block.capacity > 0) {
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
break;
case TOY_AST_NODE_COMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
if (node->compound.capacity > 0) {
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
break;
case TOY_AST_NODE_PAIR:
@@ -71,10 +75,12 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
break;
case TOY_AST_NODE_FN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
if (node->fnCollection.capacity > 0) {
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case TOY_AST_NODE_FN_DECL:
+154 -35
View File
@@ -279,6 +279,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//second and third are bad args to dictionaries
if (!TOY_IS_NULL(second) || !TOY_IS_NULL(third)) {
interpreter->errorOutput("Index slicing not allowed for dictionaries\n");
@@ -401,6 +412,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
interpreter->errorOutput("Bad first indexing\n");
@@ -543,6 +565,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
interpreter->errorOutput("Bad first indexing assignment\n");
@@ -588,7 +621,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
//simple indexing assignment if second is null
if (TOY_IS_NULL(second)) {
bool ret = -1;
int ret = -1;
if (!Toy_setLiteralArray(TOY_AS_ARRAY(compound), first, assign)) {
interpreter->errorOutput("Array index out of bounds in assignment");
@@ -596,6 +629,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
}
else {
Toy_pushLiteralArray(&interpreter->stack, compound); //leave the array on the stack
//...
ret = 1;
}
@@ -705,6 +739,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
Toy_Literal value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
if (TOY_IS_STRING(op) && Toy_equalsRefStringCString(TOY_AS_STRING(op), "+=")) {
@@ -793,6 +838,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
interpreter->errorOutput("Bad first indexing in string\n");
@@ -937,6 +993,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
interpreter->errorOutput("Bad first indexing in string assignment\n");
@@ -1064,7 +1131,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 3) {
interpreter->errorOutput("Incorrect number of arguments to _set\n");
interpreter->errorOutput("Incorrect number of arguments to set\n");
return -1;
}
@@ -1074,12 +1141,16 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_Literal val = arguments->literals[2];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("Expected identifier in _set\n");
interpreter->errorOutput("Expected identifier in set\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
bool freeKey = false;
if (TOY_IS_IDENTIFIER(key)) {
Toy_parseIdentifierToValue(interpreter, &key);
@@ -1092,26 +1163,40 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
freeVal = true;
}
if (TOY_IS_IDENTIFIER(key) || TOY_IS_IDENTIFIER(val)) {
if (freeKey) {
Toy_freeLiteral(key);
}
if (freeVal) {
Toy_freeLiteral(val);
}
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, key);
//check the subtype of the array, if there is one, against the given argument
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, idn);
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
interpreter->errorOutput("Bad argument type in _set\n");
interpreter->errorOutput("Bad argument type in set\n");
Toy_freeLiteral(typeLiteral);
return -1;
}
}
Toy_freeLiteral(typeLiteral);
if (!TOY_IS_INTEGER(key)) {
interpreter->errorOutput("Expected integer index in _set\n");
interpreter->errorOutput("Expected integer index in set\n");
return -1;
}
if (TOY_AS_ARRAY(obj)->count <= TOY_AS_INTEGER(key) || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in _set\n");
if (TOY_AS_INTEGER(key) >= TOY_AS_ARRAY(obj)->count || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in set\n");
return -1;
}
@@ -1120,7 +1205,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
TOY_AS_ARRAY(obj)->literals[TOY_AS_INTEGER(key)] = Toy_copyLiteral(val);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to array in _set: \"");
interpreter->errorOutput("Incorrect type assigned to array in set: \"");
Toy_printLiteralCustom(val, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1137,12 +1222,12 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_Literal valSubtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1];
if (TOY_AS_TYPE(keySubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(keySubtypeLiteral).typeOf != key.type) {
interpreter->printOutput("bad argument type in _set\n");
interpreter->printOutput("bad argument type in set\n");
return -1;
}
if (TOY_AS_TYPE(valSubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(valSubtypeLiteral).typeOf != val.type) {
interpreter->printOutput("bad argument type in _set\n");
interpreter->printOutput("bad argument type in set\n");
return -1;
}
}
@@ -1150,7 +1235,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_setLiteralDictionary(TOY_AS_DICTIONARY(obj), key, val);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to dictionary in _set: \"");
interpreter->errorOutput("Incorrect type assigned to dictionary in set: \"");
Toy_printLiteralCustom(val, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1160,7 +1245,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
}
default:
interpreter->errorOutput("Incorrect compound type in _set: ");
interpreter->errorOutput("Incorrect compound type in set: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1182,7 +1267,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _get");
interpreter->errorOutput("Incorrect number of arguments to get");
return -1;
}
@@ -1201,15 +1286,25 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
freeKey = true;
}
if (TOY_IS_IDENTIFIER(obj) || TOY_IS_IDENTIFIER(key)) {
if (freeObj) {
Toy_freeLiteral(obj);
}
if (freeKey) {
Toy_freeLiteral(key);
}
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
if (!TOY_IS_INTEGER(key)) {
interpreter->errorOutput("Expected integer index in _get\n");
interpreter->errorOutput("Expected integer index in get\n");
return -1;
}
if (TOY_AS_ARRAY(obj)->count <= TOY_AS_INTEGER(key) || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in _get\n");
if (TOY_AS_INTEGER(key) >= TOY_AS_ARRAY(obj)->count || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in get\n");
return -1;
}
@@ -1243,7 +1338,7 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
}
default:
interpreter->errorOutput("Incorrect compound type in _get \"");
interpreter->errorOutput("Incorrect compound type in get \"");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1253,7 +1348,7 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _push\n");
interpreter->errorOutput("Incorrect number of arguments to push\n");
return -1;
}
@@ -1262,35 +1357,47 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal val = arguments->literals[1];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("Expected identifier in _push\n");
interpreter->errorOutput("Expected identifier in push\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
bool freeVal = false;
if (TOY_IS_IDENTIFIER(val)) {
Toy_parseIdentifierToValue(interpreter, &val);
freeVal = true;
}
if (TOY_IS_IDENTIFIER(val)) {
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, val);
//check the subtype of the array, if there is one, against the given argument
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, idn);
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
interpreter->errorOutput("Bad argument type in _push");
interpreter->errorOutput("Bad argument type in push\n");
Toy_freeLiteral(typeLiteral);
return -1;
}
}
Toy_freeLiteral(typeLiteral);
Toy_pushLiteralArray(TOY_AS_ARRAY(obj), val);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
interpreter->errorOutput("Incorrect type assigned to array in _push: \"");
interpreter->errorOutput("Incorrect type assigned to array in push: \"");
Toy_printLiteralCustom(val, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1306,7 +1413,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
}
default:
interpreter->errorOutput("Incorrect compound type in _push: ");
interpreter->errorOutput("Incorrect compound type in push: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1316,7 +1423,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _pop\n");
interpreter->errorOutput("Incorrect number of arguments to pop\n");
return -1;
}
@@ -1324,12 +1431,16 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_Literal obj = arguments->literals[0];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("Expected identifier in _pop\n");
interpreter->errorOutput("Expected identifier in pop\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal lit = Toy_popLiteralArray(TOY_AS_ARRAY(obj));
@@ -1337,7 +1448,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_freeLiteral(lit);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
interpreter->errorOutput("Incorrect type assigned to array in _pop: ");
interpreter->errorOutput("Incorrect type assigned to array in pop: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1349,7 +1460,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
}
default:
interpreter->errorOutput("Incorrect compound type in _pop: ");
interpreter->errorOutput("Incorrect compound type in pop: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1359,7 +1470,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _length\n");
interpreter->errorOutput("Incorrect number of arguments to length\n");
return -1;
}
@@ -1371,6 +1482,10 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
freeObj = true;
}
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal lit = TOY_TO_INTEGER_LITERAL( TOY_AS_ARRAY(obj)->count );
@@ -1394,7 +1509,7 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
}
default:
interpreter->errorOutput("Incorrect compound type in _length: ");
interpreter->errorOutput("Incorrect compound type in length: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1410,7 +1525,7 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _clear\n");
interpreter->errorOutput("Incorrect number of arguments to clear\n");
return -1;
}
@@ -1418,12 +1533,16 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal obj = arguments->literals[0];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("expected identifier in _clear\n");
interpreter->errorOutput("expected identifier in clear\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
//NOTE: just pass in new compounds
switch(obj.type) {
@@ -1434,7 +1553,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal obj = TOY_TO_ARRAY_LITERAL(array);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to array in _clear: ");
interpreter->errorOutput("Incorrect type assigned to array in clear: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1452,7 +1571,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal obj = TOY_TO_DICTIONARY_LITERAL(dictionary);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to dictionary in _clear: ");
interpreter->errorOutput("Incorrect type assigned to dictionary in clear: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1464,7 +1583,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
}
default:
interpreter->errorOutput("Incorrect compound type in _clear: ");
interpreter->errorOutput("Incorrect compound type in clear: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
+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");
}
+7 -4
View File
@@ -5,9 +5,9 @@
#include <stdint.h>
#define TOY_VERSION_MAJOR 1
#define TOY_VERSION_MINOR 0
#define TOY_VERSION_PATCH 0
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
#define TOY_VERSION_MINOR 1
#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;
+111 -8
View File
@@ -130,7 +130,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
}
//push the store to the cache, with instructions about how pack it
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(store);
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL((Toy_LiteralDictionary*)store); //cast from array to dict, because it's intermediate
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it - nested in a dictionary
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
Toy_freeLiteral(literal);
@@ -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);
+275 -158
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) {
@@ -231,6 +236,12 @@ static bool execAssert(Toy_Interpreter* interpreter) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_IDENTIFIER(lhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
if (!TOY_IS_STRING(rhs)) {
interpreter->errorOutput("The assert keyword needs a string as the second argument, received: ");
Toy_printLiteralCustom(rhs, interpreter->errorOutput);
@@ -265,6 +276,11 @@ static bool execPrint(Toy_Interpreter* interpreter) {
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(lit)) {
Toy_freeLiteral(lit);
return false;
}
Toy_printLiteralCustom(lit, interpreter->printOutput);
Toy_freeLiteral(lit);
@@ -297,6 +313,11 @@ static bool rawLiteral(Toy_Interpreter* interpreter) {
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(lit)) {
Toy_freeLiteral(lit);
return false;
}
Toy_pushLiteralArray(&interpreter->stack, lit);
Toy_freeLiteral(lit);
@@ -312,6 +333,11 @@ static bool execNegate(Toy_Interpreter* interpreter) {
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(lit)) {
Toy_freeLiteral(lit);
return false;
}
if (TOY_IS_INTEGER(lit)) {
lit = TOY_TO_INTEGER_LITERAL(-TOY_AS_INTEGER(lit));
}
@@ -343,6 +369,11 @@ static bool execInvert(Toy_Interpreter* interpreter) {
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(lit)) {
Toy_freeLiteral(lit);
return false;
}
if (TOY_IS_BOOLEAN(lit)) {
lit = TOY_TO_BOOLEAN_LITERAL(!TOY_AS_BOOLEAN(lit));
}
@@ -376,6 +407,12 @@ static bool execArithmetic(Toy_Interpreter* interpreter, Toy_Opcode opcode) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_IDENTIFIER(lhs) || TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
//special case for string concatenation ONLY
if (TOY_IS_STRING(lhs) && TOY_IS_STRING(rhs) && (opcode == TOY_OP_ADDITION || opcode == TOY_OP_VAR_ADDITION_ASSIGN)) {
//check for overflow
@@ -508,6 +545,10 @@ static Toy_Literal parseTypeToValue(Toy_Interpreter* interpreter, Toy_Literal ty
Toy_freeLiteral(typeIdn);
}
if (TOY_IS_IDENTIFIER(type)) {
return TOY_TO_NULL_LITERAL;
}
//if this is an array or dictionary, continue to the subtypes
if (TOY_IS_TYPE(type) && (TOY_AS_TYPE(type).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(type).typeOf == TOY_LITERAL_DICTIONARY)) {
for (int i = 0; i < TOY_AS_TYPE(type).count; i++) {
@@ -548,6 +589,12 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) {
Toy_freeLiteral(typeIdn);
}
if (TOY_IS_IDENTIFIER(type)) {
Toy_freeLiteral(identifier);
Toy_freeLiteral(type);
return false;
}
//BUGFIX: because identifiers are getting embedded in type definitions
type = parseTypeToValue(interpreter, type);
@@ -565,6 +612,13 @@ static bool execVarDecl(Toy_Interpreter* interpreter, bool lng) {
Toy_freeLiteral(valIdn);
}
if (TOY_IS_IDENTIFIER(val)) {
Toy_freeLiteral(identifier);
Toy_freeLiteral(type);
Toy_freeLiteral(val);
return false;
}
if (TOY_IS_ARRAY(val) || TOY_IS_DICTIONARY(val)) {
Toy_parseCompoundToPureValues(interpreter, &val);
}
@@ -645,6 +699,12 @@ static bool execVarAssign(Toy_Interpreter* interpreter) {
Toy_freeLiteral(rhsIdn);
}
if (TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
if (TOY_IS_ARRAY(rhs) || TOY_IS_DICTIONARY(rhs)) {
Toy_parseCompoundToPureValues(interpreter, &rhs);
}
@@ -714,6 +774,12 @@ static bool execValCast(Toy_Interpreter* interpreter) {
Toy_freeLiteral(valueIdn);
}
if (TOY_IS_IDENTIFIER(value)) {
Toy_freeLiteral(type);
Toy_freeLiteral(value);
return false;
}
Toy_Literal result = TOY_TO_NULL_LITERAL;
if (TOY_IS_NULL(value)) {
@@ -848,6 +914,12 @@ static bool execCompareEqual(Toy_Interpreter* interpreter, bool invert) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_IDENTIFIER(lhs) || TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
bool result = Toy_literalsAreEqual(lhs, rhs);
if (invert) {
@@ -876,6 +948,12 @@ static bool execCompareLess(Toy_Interpreter* interpreter, bool invert) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_IDENTIFIER(lhs) || TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
//not a number, return falure
if (!(TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs))) {
interpreter->errorOutput("Incorrect type in comparison, value \"");
@@ -936,6 +1014,12 @@ static bool execCompareLessEqual(Toy_Interpreter* interpreter, bool invert) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_IDENTIFIER(lhs) || TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
//not a number, return falure
if (!(TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs))) {
interpreter->errorOutput("Incorrect type in comparison, value \"");
@@ -997,11 +1081,18 @@ static bool execAnd(Toy_Interpreter* interpreter) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_TRUTHY(lhs) && TOY_IS_TRUTHY(rhs)) {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(true));
if (TOY_IS_IDENTIFIER(lhs) || TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
//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);
@@ -1024,11 +1115,18 @@ static bool execOr(Toy_Interpreter* interpreter) {
Toy_freeLiteral(lhsIdn);
}
if (TOY_IS_TRUTHY(lhs) || TOY_IS_TRUTHY(rhs)) {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(true));
if (TOY_IS_IDENTIFIER(lhs) || TOY_IS_IDENTIFIER(rhs)) {
Toy_freeLiteral(lhs);
Toy_freeLiteral(rhs);
return false;
}
//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);
@@ -1067,6 +1165,11 @@ static bool execFalseJump(Toy_Interpreter* interpreter) {
Toy_freeLiteral(litIdn);
}
if (TOY_IS_IDENTIFIER(lit)) {
Toy_freeLiteral(lit);
return false;
}
if (TOY_IS_NULL(lit)) {
interpreter->errorOutput("Null detected in comparison\n");
Toy_freeLiteral(lit);
@@ -1090,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;
@@ -1126,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)) {
@@ -1168,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;
}
@@ -1183,7 +1281,7 @@ bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_Liter
int returnsCount = TOY_AS_FUNCTION_NATIVE(func)(interpreter, arguments);
if (returnsCount < 0) {
interpreter->errorOutput("Unknown error from native function\n");
// interpreter->errorOutput("Unknown error from native function\n");
return false;
}
@@ -1220,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;
@@ -1287,6 +1385,17 @@ bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_Liter
Toy_freeLiteral(argIdn);
}
if (TOY_IS_IDENTIFIER(arg)) {
//free, and skip out
Toy_freeLiteral(arg);
Toy_popScope(inner.scope);
Toy_freeLiteralArray(&inner.stack);
Toy_freeLiteralArray(&inner.literalCache);
return false;
}
if (!Toy_setScopeVariable(inner.scope, paramArray->literals[i], arg, false)) {
interpreter->errorOutput("[internal] Could not define parameter (bad type?)\n");
@@ -1455,6 +1564,12 @@ static bool execFnReturn(Toy_Interpreter* interpreter) {
Toy_freeLiteral(litIdn);
}
if (TOY_IS_IDENTIFIER(lit)) {
Toy_freeLiteralArray(&returns);
Toy_freeLiteral(lit);
return false;
}
if (TOY_IS_ARRAY(lit) || TOY_IS_DICTIONARY(lit)) {
Toy_parseCompoundToPureValues(interpreter, &lit);
}
@@ -1526,8 +1641,19 @@ static bool execIndex(Toy_Interpreter* interpreter, bool assignIntermediate) {
freeIdn = true;
}
if (TOY_IS_IDENTIFIER(compound)) {
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
return true;
}
if (!TOY_IS_ARRAY(compound) && !TOY_IS_DICTIONARY(compound) && !TOY_IS_STRING(compound)) {
interpreter->errorOutput("Unknown compound found in indexing notation: ");
interpreter->errorOutput("Unknown compound found in index notation: ");
Toy_printLiteralCustom(compound, interpreter->errorOutput);
interpreter->errorOutput("\n");
@@ -1565,7 +1691,7 @@ static bool execIndex(Toy_Interpreter* interpreter, bool assignIntermediate) {
Toy_pushLiteralArray(&interpreter->stack, third);
}
//call the _index function
//call the index function
if (Toy_private_index(interpreter, &arguments) < 0) {
interpreter->errorOutput("Something went wrong while indexing (simple index): ");
Toy_printLiteralCustom(compoundIdn, interpreter->errorOutput);
@@ -1596,64 +1722,101 @@ static bool execIndex(Toy_Interpreter* interpreter, bool assignIntermediate) {
return true;
}
static bool execIndexAssign(Toy_Interpreter* interpreter) {
static bool execIndexAssign(Toy_Interpreter* interpreter, int assignDepth) {
//assume -> compound, first, second, third, assign are all on the stack
Toy_Literal assign = Toy_popLiteralArray(&interpreter->stack);
Toy_Literal third = Toy_popLiteralArray(&interpreter->stack);
Toy_Literal second = Toy_popLiteralArray(&interpreter->stack);
Toy_Literal first = Toy_popLiteralArray(&interpreter->stack);
Toy_Literal compound = Toy_popLiteralArray(&interpreter->stack);
Toy_Literal assignIdn = assign;
if (TOY_IS_IDENTIFIER(assign) && Toy_parseIdentifierToValue(interpreter, &assign)) {
Toy_freeLiteral(assignIdn);
}
Toy_Literal compoundIdn = compound;
Toy_Literal assign = TOY_TO_NULL_LITERAL, third = TOY_TO_NULL_LITERAL, second = TOY_TO_NULL_LITERAL, first = TOY_TO_NULL_LITERAL, compound = TOY_TO_NULL_LITERAL, result = TOY_TO_NULL_LITERAL;
Toy_Literal compoundIdn = TOY_TO_NULL_LITERAL;
bool freeIdn = false;
if (TOY_IS_IDENTIFIER(compound) && Toy_parseIdentifierToValue(interpreter, &compound)) {
freeIdn = true;
//build the opcode
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
char* opStr = "";
switch (opcode) {
case TOY_OP_VAR_ASSIGN:
opStr = "=";
break;
case TOY_OP_VAR_ADDITION_ASSIGN:
opStr = "+=";
break;
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
opStr = "-=";
break;
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
opStr = "*=";
break;
case TOY_OP_VAR_DIVISION_ASSIGN:
opStr = "/=";
break;
case TOY_OP_VAR_MODULO_ASSIGN:
opStr = "%=";
break;
default:
interpreter->errorOutput("bad opcode in index assigning notation\n");
return false;
}
if (!TOY_IS_ARRAY(compound) && !TOY_IS_DICTIONARY(compound) && !TOY_IS_STRING(compound)) {
interpreter->errorOutput("Unknown compound found in index assigning notation\n");
//iterate...
while(assignDepth-- >= 0) {
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
if (TOY_IS_NULL(result)) {
assign = Toy_popLiteralArray(&interpreter->stack);
}
else {
assign = result;
}
return false;
}
//build the opcode
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
char* opStr = "";
switch(opcode) {
case TOY_OP_VAR_ASSIGN:
opStr = "=";
break;
case TOY_OP_VAR_ADDITION_ASSIGN:
opStr = "+=";
break;
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
opStr = "-=";
break;
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
opStr = "*=";
break;
case TOY_OP_VAR_DIVISION_ASSIGN:
opStr = "/=";
break;
case TOY_OP_VAR_MODULO_ASSIGN:
opStr = "%=";
break;
third = Toy_popLiteralArray(&interpreter->stack);
second = Toy_popLiteralArray(&interpreter->stack);
first = Toy_popLiteralArray(&interpreter->stack);
compound = Toy_popLiteralArray(&interpreter->stack);
default:
interpreter->errorOutput("bad opcode in index assigning notation\n");
if (TOY_IS_IDENTIFIER(compound)) {
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
compoundIdn = compound;
Toy_parseIdentifierToValue(interpreter, &compound);
freeIdn = true;
}
if (TOY_IS_IDENTIFIER(compound)) {
Toy_freeLiteral(compound);
Toy_freeLiteral(first);
Toy_freeLiteral(second);
Toy_freeLiteral(third);
Toy_freeLiteral(assign);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
return false;
}
Toy_Literal assignIdn = assign;
if (TOY_IS_IDENTIFIER(assign) && Toy_parseIdentifierToValue(interpreter, &assign)) {
Toy_freeLiteral(assignIdn);
}
if (TOY_IS_IDENTIFIER(assign)) {
Toy_freeLiteral(compound);
Toy_freeLiteral(first);
Toy_freeLiteral(second);
Toy_freeLiteral(third);
Toy_freeLiteral(assign);
return false;
}
if (!TOY_IS_ARRAY(compound) && !TOY_IS_DICTIONARY(compound) && !TOY_IS_STRING(compound)) {
interpreter->errorOutput("Unknown compound found in index assigning notation: ");
Toy_printLiteralCustom(compound, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
@@ -1663,103 +1826,60 @@ static bool execIndexAssign(Toy_Interpreter* interpreter) {
Toy_freeLiteral(compoundIdn);
}
return false;
}
int opLength = strlen(opStr);
Toy_Literal op = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(opStr, opLength)); //TODO: static reference optimisation?
//build the argument list
Toy_LiteralArray arguments;
Toy_initLiteralArray(&arguments);
Toy_pushLiteralArray(&arguments, compound);
Toy_pushLiteralArray(&arguments, first);
Toy_pushLiteralArray(&arguments, second);
Toy_pushLiteralArray(&arguments, third);
Toy_pushLiteralArray(&arguments, assign); //it expects an assignment command
Toy_pushLiteralArray(&arguments, op); //it expects an assignment "opcode"
//call the _index function
if (Toy_private_index(interpreter, &arguments) < 0) {
//clean up
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
return false;
}
int opLength = strlen(opStr);
Toy_Literal op = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(opStr, opLength)); //TODO: static reference optimisation?
//save the result (assume top of the interpreter stack is the new compound value)
Toy_Literal result = Toy_popLiteralArray(&interpreter->stack);
//build the argument list
Toy_LiteralArray arguments;
Toy_initLiteralArray(&arguments);
//deep
if (!freeIdn) {
while (interpreter->stack.count > 1) {
//read the new values
Toy_freeLiteral(compound);
Toy_pushLiteralArray(&arguments, compound);
Toy_pushLiteralArray(&arguments, first);
Toy_pushLiteralArray(&arguments, second);
Toy_pushLiteralArray(&arguments, third);
Toy_pushLiteralArray(&arguments, assign); //it expects an assignment command
Toy_pushLiteralArray(&arguments, op); //it expects an assignment "opcode"
//call the index function
if (Toy_private_index(interpreter, &arguments) < 0) {
//clean up
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteralArray(&arguments);
Toy_initLiteralArray(&arguments);
Toy_freeLiteral(op);
//reuse these like an idiot
third = Toy_popLiteralArray(&interpreter->stack);
second = Toy_popLiteralArray(&interpreter->stack);
first = Toy_popLiteralArray(&interpreter->stack);
compound = Toy_popLiteralArray(&interpreter->stack);
char* opStr = "="; //shadow, but force assignment
int opLength = strlen(opStr);
op = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(opStr, opLength)); //TODO: static reference optimisation?
//assign to the idn / compound - with _index
Toy_pushLiteralArray(&arguments, compound); //
Toy_pushLiteralArray(&arguments, first);
Toy_pushLiteralArray(&arguments, second);
Toy_pushLiteralArray(&arguments, third);
Toy_pushLiteralArray(&arguments, result);
Toy_pushLiteralArray(&arguments, op);
if (Toy_private_index(interpreter, &arguments) < 0) {
interpreter->errorOutput("Something went wrong while indexing (index assign): ");
Toy_printLiteralCustom(compound, interpreter->errorOutput);
interpreter->errorOutput("\n");
//clean up
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
return false;
Toy_freeLiteral(compound);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(result);
result = Toy_popLiteralArray(&interpreter->stack);
return false;
}
Toy_freeLiteral(compound);
compound = Toy_popLiteralArray(&interpreter->stack);
compoundIdn = compound;
freeIdn = false;
//save the result (assume top of the interpreter stack is the new compound value)
result = Toy_popLiteralArray(&interpreter->stack);
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
//if we loop, then we need to be assigning
opStr = "=";
}
//BUGFIX: make sure the compound name can be assigned
if (TOY_IS_NULL(compoundIdn)) {
compoundIdn = Toy_popLiteralArray(&interpreter->stack);
freeIdn = true;
}
if (TOY_IS_IDENTIFIER(compoundIdn) && !Toy_setScopeVariable(interpreter->scope, compoundIdn, result, true)) {
interpreter->errorOutput("Incorrect type assigned to compound member ");
Toy_printLiteralCustom(compoundIdn, interpreter->errorOutput);
interpreter->errorOutput(", value: ");
Toy_printLiteralCustom(result, interpreter->errorOutput);
interpreter->errorOutput("\n");
//clean up
@@ -1771,8 +1891,6 @@ static bool execIndexAssign(Toy_Interpreter* interpreter) {
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(result);
return false;
}
@@ -1786,8 +1904,6 @@ static bool execIndexAssign(Toy_Interpreter* interpreter) {
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(result);
return true;
@@ -1800,6 +1916,9 @@ static void execInterpreter(Toy_Interpreter* interpreter) {
interpreter->codeStart = interpreter->count;
}
//BUGFIX
int intermediateAssignDepth = 0;
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
while(opcode != TOY_OP_EOF && opcode != TOY_OP_SECTION_END && !interpreter->panic) {
@@ -2014,12 +2133,14 @@ static void execInterpreter(Toy_Interpreter* interpreter) {
if (!execIndex(interpreter, true)) {
return;
}
intermediateAssignDepth++;
break;
case TOY_OP_INDEX_ASSIGN:
if (!execIndexAssign(interpreter)) {
if (!execIndexAssign(interpreter, intermediateAssignDepth)) {
return;
}
intermediateAssignDepth = 0;
break;
case TOY_OP_POP_STACK:
@@ -2302,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;
}
}
+27 -18
View File
@@ -59,10 +59,10 @@ 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)) {
if (TOY_IS_TYPE(literal) && TOY_AS_TYPE(literal).capacity > 0) {
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
}
@@ -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);
@@ -241,10 +248,10 @@ bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
// ints and floats are compatible
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
if (TOY_IS_INTEGER(lhs)) {
return TOY_AS_INTEGER(lhs) + TOY_AS_FLOAT(rhs);
return TOY_AS_INTEGER(lhs) == TOY_AS_FLOAT(rhs);
}
else {
return TOY_AS_FLOAT(lhs) + TOY_AS_INTEGER(rhs);
return TOY_AS_FLOAT(lhs) == TOY_AS_INTEGER(rhs);
}
}
@@ -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);
}
+28 -23
View File
@@ -3,11 +3,14 @@
#include "toy_common.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
//forward delcare stuff
struct Toy_Literal;
struct Toy_Interpreter;
struct Toy_LiteralArray;
struct Toy_LiteralDictionary;
struct Toy_Scope;
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
typedef void (*Toy_PrintFn)(const char*);
@@ -48,25 +51,25 @@ typedef struct Toy_Literal {
//string hash?
} string; //8
void* array; //8
void* dictionary; //8
struct Toy_LiteralArray* array; //8
struct Toy_LiteralDictionary* dictionary; //8
struct {
union {
void* bytecode; //8
Toy_RefFunction* ptr; //8
Toy_NativeFn native; //8
Toy_HookFn hook; //8
} inner; //8
void* scope; //8
struct Toy_Scope* scope; //8
} function; //16
struct { //for variable names
Toy_RefString* ptr; //8
Toy_RefString* ptr; //8
int hash; //4
} identifier; //16
} identifier; //16
struct {
void* subtypes; //8
struct Toy_Literal* subtypes; //8
Toy_LiteralType typeOf; //4
unsigned char capacity; //1
unsigned char count; //1
@@ -77,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)
@@ -110,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)
@@ -141,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);
+4 -2
View File
@@ -18,8 +18,10 @@ void Toy_freeLiteralArray(Toy_LiteralArray* array) {
Toy_freeLiteral(array->literals[i]);
}
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
Toy_initLiteralArray(array);
if (array->capacity > 0) {
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
Toy_initLiteralArray(array);
}
}
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
+24 -11
View File
@@ -17,12 +17,18 @@ static void setEntryValues(Toy_private_dictionary_entry* entry, Toy_Literal key,
}
static Toy_private_dictionary_entry* getEntryArray(Toy_private_dictionary_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
if (!capacity) {
return NULL;
}
//find "key", starting at index
unsigned int index = hash % capacity;
unsigned int start = index;
int index = hash % capacity;
int start = index;
//increment once, so it can't equal start
index = (index + 1) % capacity;
if (++index >= capacity) {
index = 0;
}
//literal probing and collision checking
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
@@ -40,7 +46,10 @@ static Toy_private_dictionary_entry* getEntryArray(Toy_private_dictionary_entry*
}
}
index = (index + 1) % capacity;
if (++index >= capacity) {
index = 0;
}
//index = (index + 1) % capacity;
}
return NULL;
@@ -69,7 +78,9 @@ static void adjustEntryCapacity(Toy_private_dictionary_entry** dictionaryHandle,
}
//clear the old array
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
if (oldCapacity > 0) {
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
}
*dictionaryHandle = newEntries;
}
@@ -122,16 +133,18 @@ static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = TOY_GROW_CAPACITY(0);
dictionary->capacity = 0;
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
dictionary->capacity = 0;
}
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
if (dictionary->capacity > 0) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
}
}
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
@@ -215,5 +228,5 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
//null & not tombstoned
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
return !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
return entry != NULL && !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
}
+6 -4
View File
@@ -1,5 +1,6 @@
#include "toy_memory.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
#include "toy_console_colors.h"
@@ -8,10 +9,10 @@
//default allocator
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
if (newSize == 0 && oldSize == 0) {
//causes issues, so just skip out with a NO-OP
return NULL;
}
//causes issues, so just skip out with a NO-OP (DISABLED for performance reasons)
// if (newSize == 0 && oldSize == 0) {
// return NULL;
// }
if (newSize == 0) {
free(pointer);
@@ -49,4 +50,5 @@ void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
allocator = fn;
Toy_setRefStringAllocatorFn(fn);
Toy_setRefFunctionAllocatorFn(fn);
}
+84 -24
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
@@ -140,7 +141,7 @@ static Toy_Opcode asType(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
static Toy_Opcode typeOf(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
Toy_ASTNode* rhs = NULL;
parsePrecedence(parser, &rhs, PREC_TERNARY);
parsePrecedence(parser, &rhs, PREC_CALL);
Toy_emitASTNodeUnary(nodeHandle, TOY_OP_TYPE_OF, rhs);
return TOY_OP_EOF;
}
@@ -341,99 +342,99 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
switch(parser->previous.type) {
//arithmetic
case TOY_TOKEN_PLUS: {
parsePrecedence(parser, nodeHandle, PREC_TERM);
parsePrecedence(parser, nodeHandle, PREC_TERM + 1);
return TOY_OP_ADDITION;
}
case TOY_TOKEN_MINUS: {
parsePrecedence(parser, nodeHandle, PREC_TERM);
parsePrecedence(parser, nodeHandle, PREC_TERM + 1);
return TOY_OP_SUBTRACTION;
}
case TOY_TOKEN_MULTIPLY: {
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
return TOY_OP_MULTIPLICATION;
}
case TOY_TOKEN_DIVIDE: {
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
return TOY_OP_DIVISION;
}
case TOY_TOKEN_MODULO: {
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
return TOY_OP_MODULO;
}
//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
@@ -810,13 +840,20 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
//eat the first
if (!match(parser, TOY_TOKEN_COLON)) {
Toy_freeASTNode(first);
first = NULL;
parsePrecedence(parser, &first, PREC_TERNARY);
match(parser, TOY_TOKEN_COLON);
readFirst = true;
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
if (!first) {
Toy_freeASTNode(first);
Toy_freeASTNode(second);
Toy_freeASTNode(third);
return TOY_OP_EOF;
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
if (readFirst) {
Toy_freeASTNode(second);
second = NULL;
@@ -832,10 +869,18 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
//eat the second
if (!match(parser, TOY_TOKEN_COLON)) {
Toy_freeASTNode(second);
second = NULL;
parsePrecedence(parser, &second, PREC_TERNARY);
match(parser, TOY_TOKEN_COLON);
}
if (!second) {
Toy_freeASTNode(first);
Toy_freeASTNode(second);
Toy_freeASTNode(third);
return TOY_OP_EOF;
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
Toy_freeASTNode(third);
third = NULL;
@@ -845,7 +890,16 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
//eat the third
Toy_freeASTNode(third);
third = NULL;
parsePrecedence(parser, &third, PREC_TERNARY);
if (!third) {
Toy_freeASTNode(first);
Toy_freeASTNode(second);
Toy_freeASTNode(third);
return TOY_OP_EOF;
}
Toy_emitASTNodeIndex(nodeHandle, first, second, third);
consume(parser, TOY_TOKEN_BRACKET_RIGHT, "Expected ']' in index notation");
@@ -894,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,
@@ -1352,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);
+58 -60
View File
@@ -4,21 +4,19 @@
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Toy_Scope* scope) {
scope->references--;
while (scope != NULL) {
Toy_Scope* next = scope->ancestor;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
scope->references--;
if (scope->references <= 0) {
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
scope = next;
}
if (scope->references > 0) {
return;
}
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
//return false if invalid type
@@ -209,6 +207,10 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
}
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
if (original == NULL) {
return NULL;
}
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
scope->ancestor = original->ancestor;
Toy_initLiteralDictionary(&scope->variables);
@@ -255,74 +257,70 @@ bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal typ
}
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
if (scope == NULL) {
return false;
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
return true;
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
return false;
}
//return false if undefined, or can't be assigned
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
while (scope != NULL) {
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
scope = scope->ancestor;
continue;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
return true;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return true;
return false;
}
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
//optimized to reduce call stack
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
return false;
}
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
//dead end
if (scope == NULL) {
return TOY_TO_NULL_LITERAL;
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getLiteralDictionary(&scope->types, key);
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getScopeType(scope->ancestor, key);
}
return Toy_getLiteralDictionary(&scope->types, key);
return TOY_TO_NULL_LITERAL;
}
+7
View File
@@ -38,5 +38,12 @@ s += "bar";
assert s == "foobar", "string addition failed (wasn't sticky enough)";
//check order of operations
assert 30 / 3 * 2 == 20, "Order of operations failed (raw numbers)";
var x = 30;
var y = 3;
var z = 2;
assert x / y * z == 20, "Order of operations failed (variables)";
print "All good";
@@ -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";
View File
+17
View File
@@ -0,0 +1,17 @@
import standard;
import random;
var generator: opaque = createRandomGenerator(clock().hash()); //create a new generator object, from a non-determinant source
var a: int = generator.generateRandomNumber();
var b: int = generator.generateRandomNumber();
var c: int = generator.generateRandomNumber();
generator.freeRandomGenerator();
assert a != b, "random a != random b failed";
assert a != c, "random a != random c failed";
assert b != c, "random b != random c failed";
print "All good";
+94
View File
@@ -7,6 +7,100 @@ import standard;
}
//test hash
{
assert typeof "Hello world".hash() == int, "typeof \"Hello world\".hash() failed";
assert "Hello world".hash() == 994097935, "\"Hello world\".hash() failed"; //NOTE: specific value based on algorithm
}
//test abs
{
assert abs(-5) == 5, "abs(-integer) failed";
assert abs(-5.5) == 5.5, "abs(-float) failed";
assert abs(5) == 5, "abs(+integer) failed";
assert abs(5.5) == 5.5, "abs(+float) failed";
var x = -5;
assert x.abs() == 5, "var.abs() failed";
}
//test ceil
{
assert ceil(4) == 4, "ceil(int) failed";
assert ceil(4.0) == 4, "ceil(float) failed";
assert ceil(4.1) == 5, "ceil() failed";
var x = 4.1;
assert x.ceil() == 5, "var.ceil() failed";
}
//test floor
{
assert floor(4) == 4, "floor(int) failed";
assert floor(4.0) == 4, "floor(float) failed";
assert floor(4.1) == 4, "floor() failed";
var x = 4.1;
assert x.floor() == 4, "var.floor() failed";
}
//test max
{
assert max(1, 2, 3) == 3, "max() failed";
var a = 1;
var b = 2;
var c = 3;
assert max(a, b, c) == 3, "var.max() failed";
assert max(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) == 9, "max() with many args failed";
assert typeof max(1, 2, 3) == int, "typeof max() == int failed";
assert typeof max(1, 2, 3.4) == float, "typeof max() == float failed";
}
//test min
{
assert min(1, 2, 3) == 1, "min() failed";
var a = 1;
var b = 2;
var c = 3;
assert min(a, b, c) == 1, "var.min() failed";
assert min(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) == 0, "min() with many args failed";
assert typeof min(1, 2, 3) == int, "typeof min() == int failed";
assert typeof min(1, 2, 3.4) == float, "typeof min() == float failed";
}
//test round
{
assert round(4) == 4, "round(int) failed";
assert round(4.0) == 4, "round(float) failed";
assert round(4.1) == 4, "round(less than half) failed";
assert round(4.9) == 5, "round(greater than half) failed";
assert round(4.5) == 5, "round(exactly half) failed";
var x = 4.1;
assert x.round() == 4, "var.round() failed";
assert typeof round(1.0) == int, "typeof round() == int failed";
}
//test concat
{
//test array concat
@@ -0,0 +1 @@
"a"[--];
@@ -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
+9 -62
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>
@@ -14,6 +15,7 @@
#include "../repl/repl_tools.h"
#include "../repl/lib_about.h"
#include "../repl/lib_random.h"
#include "../repl/lib_runner.h"
#include "../repl/lib_standard.h"
@@ -45,30 +47,15 @@ void runBinaryWithLibrary(const unsigned char* tb, size_t size, const char* libr
Toy_setInterpreterError(&interpreter, errorWrapper);
//inject the standard libraries into this interpreter
if (hook != Toy_hookStandard) {
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
}
Toy_injectNativeHook(&interpreter, library, hook);
Toy_runInterpreter(&interpreter, tb, size);
Toy_freeInterpreter(&interpreter);
}
void runBinaryQuietly(const unsigned char* tb, size_t size) {
Toy_Interpreter interpreter;
Toy_initInterpreter(&interpreter);
//NOTE: supress print output for testing
Toy_setInterpreterPrint(&interpreter, noPrintFn);
Toy_setInterpreterAssert(&interpreter, assertWrapper);
Toy_setInterpreterError(&interpreter, errorWrapper);
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
Toy_runInterpreter(&interpreter, tb, size);
Toy_freeInterpreter(&interpreter);
}
typedef struct Payload {
char* fname;
char* libname;
@@ -77,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
@@ -94,6 +75,7 @@ int main() {
{"about.toy", "about", Toy_hookAbout},
{"standard.toy", "standard", Toy_hookStandard},
{"runner.toy", "runner", Toy_hookRunner},
{"random.toy", "random", Toy_hookRandom},
{NULL, NULL, NULL}
};
@@ -125,43 +107,8 @@ int main() {
}
}
{
//run whatever, testing stuff together to check for memory leaks
char* whatever[] = {
"random-stuff.toy",
NULL
};
for (int i = 0; whatever[i]; i++) {
printf("Running %s\n", whatever[i]);
char fname[128];
snprintf(fname, 128, "scripts/lib/%s", whatever[i]);
//compile the source
size_t size = 0;
const char* source = (const char*)Toy_readFile(fname, &size);
if (!source) {
printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname);
failedAsserts++;
continue;
}
const unsigned char* tb = Toy_compileString(source, &size);
free((void*)source);
if (!tb) {
printf(TOY_CC_ERROR "Failed to compile file: %s\n" TOY_CC_RESET, fname);
failedAsserts++;
continue;
}
runBinaryQuietly(tb, size);
}
}
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
if (!failedAsserts) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
+1
View File
@@ -101,6 +101,7 @@ int main() {
"declare-types-array.toy",
"declare-types-dictionary-key.toy",
"declare-types-dictionary-value.toy",
"index-access-bugfix.toy",
"index-arrays-non-integer.toy",
"string-concat.toy",
"unary-inverted-nothing.toy",
+11 -4
View File
@@ -10,25 +10,32 @@
int currentMemoryUsed = 0;
int maxMemoryUsed = 0;
int memoryAllocCalls = 0;
int memoryAllocFree = 0;
int memoryAllocRealloc = 0;
static void* trackerAllocator(void* pointer, size_t oldSize, size_t newSize) {
//the number of raw calls
memoryAllocCalls++;
//causes issues, so just skip out with a NO-OP
if (newSize == 0 && oldSize == 0) {
//causes issues, so just skip out with a NO-OP
return NULL;
}
memoryAllocCalls++;
//track the changes
currentMemoryUsed = currentMemoryUsed - oldSize + newSize;
maxMemoryUsed = currentMemoryUsed > maxMemoryUsed ? currentMemoryUsed : maxMemoryUsed;
if (newSize == 0) {
//the number of frees
memoryAllocFree++;
free(pointer);
return NULL;
}
//the number of reallocations
memoryAllocRealloc++;
void* mem = realloc(pointer, newSize);
if (mem == NULL) {
@@ -69,7 +76,7 @@ int main(int argc, const char* argv[]) {
Toy_freeDriveDictionary();
//report output
printf("Memory report: %d max bytes, %d calls\n", maxMemoryUsed, memoryAllocCalls);
printf("Heap Memory Report:\n\t%d max bytes\n\t%d calls to the allocator\n\t%d calls to realloc()\n\t%d calls to free()\n\t%d discrepancies\n", maxMemoryUsed, memoryAllocCalls, memoryAllocRealloc, memoryAllocFree, memoryAllocCalls - memoryAllocRealloc - memoryAllocFree);
return 0;
}