Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f6ec6a8c73 | |||
| 2157b2f540 | |||
| 1481216e69 | |||
| f25f389b4e | |||
| deff784df8 | |||
| 54e82846c3 | |||
| 67fce427eb | |||
| 8a2cb61435 | |||
| 50d03e28fc | |||
| 763581c73b | |||
| cdb2613e5d | |||
| 733df87c08 | |||
| bfd506f497 | |||
| 18b59c9e84 | |||
| d3eb31d964 | |||
| 07f4a98b95 | |||
| 0949fd6ff9 | |||
| 03e5096f10 | |||
| bb81b8c474 | |||
| cf6db57787 | |||
| 17f0e4476b | |||
| 1095e1a885 | |||
| 2edfbbe3ef | |||
| 4b83f1f0d6 | |||
| e2fa1cf2e8 | |||
| a04d2c4816 | |||
| f2f8aed23a | |||
| 68ed52b347 | |||
| 88dac53ae0 | |||
| f84cdff883 | |||
| f869c9425a | |||
| 76ddd5703e | |||
| 669808730e | |||
| e6d9809da5 | |||
| 502032e514 | |||
| 6e9d42f892 | |||
| 70ca27486e | |||
| 12fa434e0f | |||
| efc1e764d2 | |||
| c5c0122243 | |||
| 348b7b8c24 | |||
| e243ad949a | |||
| 9b673f23ad | |||
| 624a0c80ba | |||
| 1064b69d04 | |||
| e9b347acb6 | |||
| 071c8da2aa | |||
| d6538812bf | |||
| 3aeddff736 | |||
| c88c1b125d | |||
| 1513ba9878 | |||
| bc0289c3f4 | |||
| 92c71a374d | |||
| e0547474b8 | |||
| 3e6d21afbb | |||
| d3df01c1c4 | |||
| cdca6fa45c | |||
| 1dde9d8f29 | |||
| 7f0f17b6e0 | |||
| 3507104121 |
@@ -19,6 +19,8 @@ bin/
|
||||
*.db
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.dll
|
||||
*.exe
|
||||
*.meta
|
||||
*.log
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -12,3 +12,4 @@ void Toy_runBinaryFile(const char* fname);
|
||||
void Toy_runSource(const char* source);
|
||||
void Toy_runSourceFile(const char* fname);
|
||||
|
||||
void Toy_parseBinaryFileHeader(const char* fname);
|
||||
@@ -1,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";
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
var days = [
|
||||
"sunday",
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday"
|
||||
];
|
||||
|
||||
var rng = 10; //for chosen at random
|
||||
|
||||
|
||||
var index = rng % days.length();
|
||||
|
||||
assert index == 3, "dot modulo bugfix failed";
|
||||
|
||||
rng %= days.length();
|
||||
|
||||
assert rng == 3, "dot modulo assign bugfix failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
fn a() {
|
||||
fn b() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
assert a()() == 42, "function within function failed";
|
||||
}
|
||||
|
||||
{
|
||||
fn a() {
|
||||
fn b() {
|
||||
fn c() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
assert a()()() == 42, "function within function within function failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
var result; //result must exist to ensure assingment, rather than declaration is invoked by the comparison below
|
||||
|
||||
var lhs = [0];
|
||||
var rhs = [0];
|
||||
|
||||
result = lhs[0] < rhs[0]; //make sure this doesn't invoke TOY_OP_INDEX_ASSIGN_INTERMEDIATE
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user