Compare commits

..

26 Commits

Author SHA1 Message Date
Kayne Ruse f84cdff883 Fixed order of operations 2023-03-07 06:49:17 +11:00
Ratstail91 f869c9425a Corrected an error message 2023-03-05 13:05:16 +11:00
Ratstail91 76ddd5703e Hack: just track the intermediate depth externally 2023-03-05 00:24:07 +11:00
Ratstail91 669808730e Minor tweak that shouldn't break anything 2023-03-04 22:57:41 +11:00
Ratstail91 e6d9809da5 Famous last words: I think I fixed it 2023-03-04 22:18:17 +11:00
Ratstail91 502032e514 Testing an obscure bugfix 2023-03-04 15:41:55 +11:00
Ratstail91 6e9d42f892 Merge branch 'dev' 2023-02-28 17:39:05 +11:00
Ratstail91 70ca27486e Bugfix a leak? 2023-02-28 17:37:43 +11:00
Ratstail91 12fa434e0f Experimenting with cleaning up loopy code 2023-02-28 17:29:37 +11:00
Ratstail91 efc1e764d2 Patched a casting error in round 2023-02-27 23:27:11 +11:00
Kayne Ruse c5c0122243 BUGFIX: typeof keyword precedence was off 2023-02-27 21:47:38 +11:00
Kayne Ruse 348b7b8c24 Added some math utils to standard
* ceil
* floor
* max
* min
* round
2023-02-27 21:32:31 +11:00
Kayne Ruse e243ad949a Removed a divide instruction (modulo) from the final output, thanks Wren! 2023-02-26 22:41:58 +11:00
Ratstail91 9b673f23ad Reduced C callstack size in Toy_Scope 2023-02-26 22:31:37 +11:00
Kayne Ruse 624a0c80ba Prevented NO-OP calls to the memory allocator
Also shaved off about 1-2 milliseconds of execution time of fib-memo.toy
2023-02-26 21:20:22 +11:00
Ratstail91 1064b69d04 BUGFIX: Integer and float comparisons always return true 2023-02-26 01:27:21 +11:00
Ratstail91 e9b347acb6 MSVC + Box Engine are dumber than a bag of rocks 2023-02-25 04:40:12 +11:00
Ratstail91 071c8da2aa Visual Studio broke itself - fixed 2023-02-25 04:28:07 +11:00
Ratstail91 d6538812bf Merge branch 'main' of https://github.com/Ratstail91/Toy 2023-02-25 04:18:03 +11:00
Ratstail91 3aeddff736 Tweaks to dictionary for performance 2023-02-24 22:13:50 +11:00
Ratstail91 c88c1b125d Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-24 21:53:42 +11:00
Kayne Ruse 1513ba9878 tweaked scripts folder 2023-02-23 22:45:38 +11:00
Kayne Ruse bc0289c3f4 tweaked scripts folder 2023-02-23 20:23:10 +11:00
Kayne Ruse 92c71a374d Implemented a basic random library 2023-02-23 19:19:17 +11:00
Kayne Ruse e0547474b8 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-23 18:37:11 +11:00
Kayne Ruse 3e6d21afbb Added abs(), hash() to libstandard 2023-02-23 18:36:12 +11:00
30 changed files with 1013 additions and 541 deletions
+10 -5
View File
@@ -115,16 +115,14 @@
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>
</PreprocessorDefinitions>
<PreprocessorDefinitions>LIB_RUNNER_EXPORT</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>LIB_RUNNER_EXPORT</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>
+196
View File
@@ -0,0 +1,196 @@
#include "lib_random.h"
#include "toy_memory.h"
static int hashInt(int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
typedef struct Toy_RandomGenerator {
int seed; //mutated with each call
} Toy_RandomGenerator;
//Toy native functions
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
return -1;
}
//get the seed argument
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
Toy_Literal seedLiteralIdn = seedLiteral;
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
Toy_freeLiteral(seedLiteralIdn);
}
if (TOY_IS_IDENTIFIER(seedLiteral)) {
Toy_freeLiteral(seedLiteral);
return -1;
}
if (!TOY_IS_INTEGER(seedLiteral)) {
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
Toy_freeLiteral(seedLiteral);
return -1;
}
//generate the generator object
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
generator->seed = TOY_AS_INTEGER(seedLiteral);
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
//return and cleanup
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
Toy_freeLiteral(seedLiteral);
Toy_freeLiteral(generatorLiteral);
return 1;
}
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
return -1;
}
//get the runner object
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
Toy_Literal generatorLiteralIdn = generatorLiteral;
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
Toy_freeLiteral(generatorLiteralIdn);
}
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
Toy_freeLiteral(generatorLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
return -1;
}
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
//generate the new value and package up the return
generator->seed = hashInt(generator->seed);
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
Toy_freeLiteral(generatorLiteral);
Toy_freeLiteral(resultLiteral);
return 0;
}
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
return -1;
}
//get the runner object
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
Toy_Literal generatorLiteralIdn = generatorLiteral;
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
Toy_freeLiteral(generatorLiteralIdn);
}
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
Toy_freeLiteral(generatorLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
return -1;
}
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
//clear out the runner object
TOY_FREE(Toy_RandomGenerator, generator);
Toy_freeLiteral(generatorLiteral);
return 0;
}
//call the hook
typedef struct Natives {
const char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"createRandomGenerator", nativeCreateRandomGenerator},
{"generateRandomNumber", nativeGenerateRandomNumber},
{"freeRandomGenerator", nativeFreeRandomGenerator},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
#define TOY_OPAQUE_TAG_RANDOM 200
+25 -6
View File
@@ -5,12 +5,31 @@
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
//platform/compiler-specific instructions - because MSVC + Box Engine are dumber than a bag of rocks
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
#define LIB_RUNNER_API extern
#elif defined(_MSC_VER)
#ifndef LIB_RUNNER_EXPORT
#define LIB_RUNNER_API __declspec(dllimport)
#else
#define LIB_RUNNER_API __declspec(dllexport)
#endif
#else
#define LIB_RUNNER_API extern
#endif
//file system API - these need to be set by the host
LIB_RUNNER_API void Toy_initDriveDictionary();
LIB_RUNNER_API void Toy_freeDriveDictionary();
LIB_RUNNER_API Toy_LiteralDictionary* Toy_getDriveDictionary();
//file system API - for use with other libs
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
LIB_RUNNER_API Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+345
View File
@@ -32,6 +32,339 @@ static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
return 1;
}
static int nativeHash(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to hash\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result = TOY_TO_INTEGER_LITERAL(Toy_hashLiteral(selfLiteral));
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeAbs(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to abs\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to abs\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(selfLiteral) > 0 ? TOY_AS_INTEGER(selfLiteral) : -TOY_AS_INTEGER(selfLiteral) );
}
if (TOY_IS_FLOAT(selfLiteral)) {
result = TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(selfLiteral) > 0 ? TOY_AS_FLOAT(selfLiteral) : -TOY_AS_FLOAT(selfLiteral) );
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeCeil(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to ceil\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to ceil\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
//NO-OP
result = Toy_copyLiteral(selfLiteral);
}
if (TOY_IS_FLOAT(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL( (int)TOY_AS_FLOAT(selfLiteral) - TOY_AS_FLOAT(selfLiteral) == 0 ? (int)TOY_AS_FLOAT(selfLiteral) : (int)TOY_AS_FLOAT(selfLiteral) + 1 );
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeFloor(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to floor\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to floor\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
//NO-OP
result = Toy_copyLiteral(selfLiteral);
}
if (TOY_IS_FLOAT(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL( (int)TOY_AS_FLOAT(selfLiteral) );
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeMax(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//return value
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
//iterate over all arguments
do {
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to max\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
//if not comparing yet...
if (TOY_IS_NULL(resultLiteral)) {
resultLiteral = selfLiteral;
continue;
}
//cooerce if needed
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
resultLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(resultLiteral) );
}
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
selfLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(selfLiteral) );
}
//compare
if (TOY_IS_INTEGER(resultLiteral) && TOY_AS_INTEGER(resultLiteral) < TOY_AS_INTEGER(selfLiteral)) {
//NOTE: just ints, don't free
resultLiteral = selfLiteral;
}
else if (TOY_IS_FLOAT(resultLiteral) && TOY_AS_FLOAT(resultLiteral) < TOY_AS_FLOAT(selfLiteral)) {
//NOTE: just floats, don't free
resultLiteral = selfLiteral;
}
}
while (arguments->count > 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeMin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//return value
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
//iterate over all arguments
do {
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to min\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
//if not comparing yet...
if (TOY_IS_NULL(resultLiteral)) {
resultLiteral = selfLiteral;
continue;
}
//cooerce if needed
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
resultLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(resultLiteral) );
}
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
selfLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(selfLiteral) );
}
//compare
if (TOY_IS_INTEGER(resultLiteral) && TOY_AS_INTEGER(resultLiteral) > TOY_AS_INTEGER(selfLiteral)) {
//NOTE: just ints, don't free
resultLiteral = selfLiteral;
}
else if (TOY_IS_FLOAT(resultLiteral) && TOY_AS_FLOAT(resultLiteral) > TOY_AS_FLOAT(selfLiteral)) {
//NOTE: just floats, don't free
resultLiteral = selfLiteral;
}
}
while (arguments->count > 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeRound(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to round\n");
return -1;
}
//get the self
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
//parse to value if needed
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to round\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal result;
if (TOY_IS_INTEGER(selfLiteral)) {
//NO-OP
result = Toy_copyLiteral(selfLiteral);
}
if (TOY_IS_FLOAT(selfLiteral)) {
//catch the already-rounded case
if (TOY_AS_FLOAT(selfLiteral) == (int)TOY_AS_FLOAT(selfLiteral)) {
result = TOY_TO_INTEGER_LITERAL((int)TOY_AS_FLOAT(selfLiteral));
}
else {
result = TOY_TO_INTEGER_LITERAL( TOY_AS_FLOAT(selfLiteral) - (int)TOY_AS_FLOAT(selfLiteral) < 0.5 ? (int)TOY_AS_FLOAT(selfLiteral) : (int)TOY_AS_FLOAT(selfLiteral) + 1 );
}
}
Toy_pushLiteralArray(&interpreter->stack, result);
Toy_freeLiteral(result);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
@@ -1667,7 +2000,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
+2
View File
@@ -1,6 +1,7 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_standard.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "toy_console_colors.h"
@@ -29,6 +30,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(;;) {
+2
View File
@@ -1,6 +1,7 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_standard.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "toy_console_colors.h"
@@ -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);
-125
View File
@@ -1,125 +0,0 @@
import node;
//constants
var SPEED: int const = 10;
//variables
var parent: opaque = null;
var posX: int = 50;
var posY: int = 50;
var WIDTH: int const = 100;
var HEIGHT: int const = 100;
var xspeed: int = 0;
var yspeed: int = 0;
//accessors - variables are private, functions are public
fn getX(node: opaque) {
return posX;
}
fn getY(node: opaque) {
return posY;
}
//lifecycle functions
fn onInit(node: opaque) {
print "render.toy:onInit() called\n";
node.loadTexture("sprites:/character.png");
parent = node.getNodeParent();
}
fn onStep(node: opaque) {
posX += xspeed;
posY += yspeed;
}
fn onFree(node: opaque) {
print "render.toy:onFree() called\n";
node.freeTexture();
}
fn onDraw(node: opaque) {
// print "render.toy:onDraw() called\n";
var px = parent.callNode("getX");
var py = parent.callNode("getY");
if (px == null) {
px = 0;
}
if (py == null) {
py = 0;
}
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
}
//event functions
fn onKeyDown(node: opaque, event: string) {
if (event == "character_up") {
yspeed -= SPEED;
return;
}
if (event == "character_down") {
yspeed += SPEED;
return;
}
if (event == "character_left") {
xspeed -= SPEED;
return;
}
if (event == "character_right") {
xspeed += SPEED;
return;
}
}
fn onKeyUp(node: opaque, event: string) {
if (event == "character_up" && yspeed < 0) {
yspeed = 0;
return;
}
if (event == "character_down" && yspeed > 0) {
yspeed = 0;
return;
}
if (event == "character_left" && xspeed < 0) {
xspeed = 0;
return;
}
if (event == "character_right" && xspeed > 0) {
xspeed = 0;
return;
}
}
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
// print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")\n";
}
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")\n";
//jump to pos
posX = x - WIDTH / 2;
posY = y - HEIGHT / 2;
}
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")\n";
}
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
// print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")\n";
}
-89
View File
@@ -1,89 +0,0 @@
//single line comment
/*
multi line comment
*/
//test primitive literals
print "hello world";
print null;
print true;
print false;
print 42;
print 3.14;
print -69;
print -4.20;
print 2 + (3 * 3);
//test operators (integers)
print 1 + 1;
print 1 - 1;
print 2 * 2;
print 1 / 2;
print 4 % 2;
//test operators (floats)
print 1.0 + 1.0;
print 1.0 - 1.0;
print 2.0 * 2.0;
print 1.0 / 2.0;
//test scopes
{
print "This statement is within a scope.";
{
print "This is a deeper scope.";
}
}
print "Back to the outer scope.";
//test scope will delegate to higher scope
var a = 1;
{
a = 2;
print a;
}
print a;
//test scope will shadow higher scope on redefine
var b: int = 3;
{
var b = 4;
print b;
}
print b;
//test compounds, repeatedly
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
//test empties
print [];
print [:];
//test nested compounds
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
//var declarations
var x = 31;
var y : int = 42;
var arr : [int] = [1, 2, 3, 42];
var dict : [string:int] = ["hello": 1, "world":2];
//printing expressions
print x;
print x + y;
print arr;
print dict;
//test asserts at the end of the file
assert x, "This won't be seen";
assert true, "This won't be seen";
assert false, "This is a failed assert, and will end execution";
print "This will not be printed because of the above assert";
+3 -3
View File
@@ -34,8 +34,8 @@ var tiles: [[int]] const = [
];
var tileset: [int: string] const = [
0: " ",
1: " X "
0: " ",
1: "X "
];
//variables
@@ -48,7 +48,7 @@ fn draw() {
for (var i: int = 0; i < WIDTH; i++) {
//draw the player pos
if (i == posX && j == posY) {
print " O ";
print "O ";
continue;
}
+36
View File
@@ -0,0 +1,36 @@
/*
Since this is a pseudo-random generator, and there's no internal state to the algorithm other
than the generator opaque, there needs to be a "call counter" (current depth) to shuffle the
initial seeds, otherwise generators created from other generators will resemble their parents,
but one call greater.
*/
import standard;
import random;
var DEPTH: int const = 20;
var levels = [];
//generate the level seeds
var generator: opaque = createRandomGenerator(clock().hash());
for (var i: int = 0; i < DEPTH; i++) {
levels.push(generator.generateRandomNumber());
}
generator.freeRandomGenerator();
//generate "levels" of a roguelike
for (var i = 0; i < DEPTH; i++) {
var rng: opaque = createRandomGenerator(levels[i] + i);
print "---";
print levels[i];
print rng.generateRandomNumber();
print rng.generateRandomNumber();
print rng.generateRandomNumber();
rng.freeRandomGenerator();
}
View File
+15 -9
View File
@@ -40,17 +40,21 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
break;
case TOY_AST_NODE_BLOCK:
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
if (node->block.capacity > 0) {
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
break;
case TOY_AST_NODE_COMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
if (node->compound.capacity > 0) {
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
break;
case TOY_AST_NODE_PAIR:
@@ -71,10 +75,12 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
break;
case TOY_AST_NODE_FN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
if (node->fnCollection.capacity > 0) {
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case TOY_AST_NODE_FN_DECL:
+3 -2
View File
@@ -621,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");
@@ -629,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;
}
@@ -1385,7 +1386,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
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;
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <stdint.h>
#define TOY_VERSION_MAJOR 1
#define TOY_VERSION_MINOR 0
#define TOY_VERSION_MINOR 1
#define TOY_VERSION_PATCH 1
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
+1 -1
View File
@@ -130,7 +130,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
}
//push the store to the cache, with instructions about how pack it
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(store);
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL((Toy_LiteralDictionary*)store); //cast from array to dict, because it's intermediate
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it - nested in a dictionary
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
Toy_freeLiteral(literal);
+127 -153
View File
@@ -1651,7 +1651,7 @@ static bool execIndex(Toy_Interpreter* interpreter, bool assignIntermediate) {
}
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");
@@ -1689,7 +1689,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);
@@ -1720,85 +1720,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);
}
if (TOY_IS_IDENTIFIER(assign)) {
Toy_freeLiteral(compound);
Toy_freeLiteral(first);
Toy_freeLiteral(second);
Toy_freeLiteral(third);
Toy_freeLiteral(assign);
return false;
}
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;
}
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;
}
if (!TOY_IS_ARRAY(compound) && !TOY_IS_DICTIONARY(compound) && !TOY_IS_STRING(compound)) {
interpreter->errorOutput("Unknown compound found in index assigning notation\n");
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
return false;
}
//build the opcode
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
char* opStr = "";
switch(opcode) {
case TOY_OP_VAR_ASSIGN:
opStr = "=";
switch (opcode) {
case TOY_OP_VAR_ASSIGN:
opStr = "=";
break;
case TOY_OP_VAR_ADDITION_ASSIGN:
opStr = "+=";
case TOY_OP_VAR_ADDITION_ASSIGN:
opStr = "+=";
break;
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
opStr = "-=";
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
opStr = "-=";
break;
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
opStr = "*=";
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
opStr = "*=";
break;
case TOY_OP_VAR_DIVISION_ASSIGN:
opStr = "/=";
case TOY_OP_VAR_DIVISION_ASSIGN:
opStr = "/=";
break;
case TOY_OP_VAR_MODULO_ASSIGN:
opStr = "%=";
case TOY_OP_VAR_MODULO_ASSIGN:
opStr = "%=";
break;
default:
interpreter->errorOutput("bad opcode in index assigning notation\n");
default:
interpreter->errorOutput("bad opcode in index assigning notation\n");
return false;
}
//iterate...
while(assignDepth-- >= 0) {
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
if (TOY_IS_NULL(result)) {
assign = Toy_popLiteralArray(&interpreter->stack);
}
else {
assign = result;
}
third = Toy_popLiteralArray(&interpreter->stack);
second = Toy_popLiteralArray(&interpreter->stack);
first = Toy_popLiteralArray(&interpreter->stack);
compound = Toy_popLiteralArray(&interpreter->stack);
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);
@@ -1808,103 +1824,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
@@ -1916,8 +1889,6 @@ static bool execIndexAssign(Toy_Interpreter* interpreter) {
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(result);
return false;
}
@@ -1931,8 +1902,6 @@ static bool execIndexAssign(Toy_Interpreter* interpreter) {
if (freeIdn) {
Toy_freeLiteral(compoundIdn);
}
Toy_freeLiteral(op);
Toy_freeLiteralArray(&arguments);
Toy_freeLiteral(result);
return true;
@@ -1945,6 +1914,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) {
@@ -2159,12 +2131,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:
+3 -3
View File
@@ -62,7 +62,7 @@ void Toy_freeLiteral(Toy_Literal literal) {
TOY_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(literal));
}
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]);
}
@@ -241,10 +241,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);
}
}
+7 -4
View File
@@ -8,6 +8,8 @@
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,8 +50,8 @@ 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 {
@@ -57,7 +59,7 @@ typedef struct Toy_Literal {
Toy_NativeFn native; //8
Toy_HookFn hook; //8
} inner; //8
void* scope; //8
struct Toy_Scope* scope; //8
} function; //16
struct { //for variable names
@@ -66,7 +68,8 @@ typedef struct Toy_Literal {
} identifier; //16
struct {
void* subtypes; //8
struct Toy_Literal* subtypes; //8
Toy_LiteralType typeOf; //4
unsigned char capacity; //1
unsigned char count; //1
+4 -2
View File
@@ -18,8 +18,10 @@ void Toy_freeLiteralArray(Toy_LiteralArray* array) {
Toy_freeLiteral(array->literals[i]);
}
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
Toy_initLiteralArray(array);
if (array->capacity > 0) {
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
Toy_initLiteralArray(array);
}
}
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
+24 -11
View File
@@ -17,12 +17,18 @@ static void setEntryValues(Toy_private_dictionary_entry* entry, Toy_Literal key,
}
static Toy_private_dictionary_entry* getEntryArray(Toy_private_dictionary_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
if (!capacity) {
return NULL;
}
//find "key", starting at index
unsigned int index = hash % capacity;
unsigned int start = index;
int index = hash % capacity;
int start = index;
//increment once, so it can't equal start
index = (index + 1) % capacity;
if (++index >= capacity) {
index = 0;
}
//literal probing and collision checking
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
@@ -40,7 +46,10 @@ static Toy_private_dictionary_entry* getEntryArray(Toy_private_dictionary_entry*
}
}
index = (index + 1) % capacity;
if (++index >= capacity) {
index = 0;
}
//index = (index + 1) % capacity;
}
return NULL;
@@ -69,7 +78,9 @@ static void adjustEntryCapacity(Toy_private_dictionary_entry** dictionaryHandle,
}
//clear the old array
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
if (oldCapacity > 0) {
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
}
*dictionaryHandle = newEntries;
}
@@ -122,16 +133,18 @@ static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = TOY_GROW_CAPACITY(0);
dictionary->capacity = 0;
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
dictionary->capacity = 0;
}
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
if (dictionary->capacity > 0) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
}
}
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
@@ -215,5 +228,5 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
//null & not tombstoned
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
return !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
return entry != NULL && !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
}
+4 -4
View File
@@ -8,10 +8,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);
+6 -6
View File
@@ -140,7 +140,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,27 +341,27 @@ 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;
}
+58 -60
View File
@@ -4,21 +4,19 @@
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Toy_Scope* scope) {
scope->references--;
while (scope != NULL) {
Toy_Scope* next = scope->ancestor;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
scope->references--;
if (scope->references <= 0) {
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
scope = next;
}
if (scope->references > 0) {
return;
}
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
//return false if invalid type
@@ -209,6 +207,10 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
}
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
if (original == NULL) {
return NULL;
}
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
scope->ancestor = original->ancestor;
Toy_initLiteralDictionary(&scope->variables);
@@ -255,74 +257,70 @@ bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal typ
}
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
if (scope == NULL) {
return false;
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
return true;
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
return false;
}
//return false if undefined, or can't be assigned
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
while (scope != NULL) {
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
scope = scope->ancestor;
continue;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
return true;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return true;
return false;
}
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
//optimized to reduce call stack
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
return false;
}
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
//dead end
if (scope == NULL) {
return TOY_TO_NULL_LITERAL;
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getLiteralDictionary(&scope->types, key);
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getScopeType(scope->ancestor, key);
}
return Toy_getLiteralDictionary(&scope->types, key);
return TOY_TO_NULL_LITERAL;
}
+7
View File
@@ -38,5 +38,12 @@ s += "bar";
assert s == "foobar", "string addition failed (wasn't sticky enough)";
//check order of operations
assert 30 / 3 * 2 == 20, "Order of operations failed (raw numbers)";
var x = 30;
var y = 3;
var z = 2;
assert x / y * z == 20, "Order of operations failed (variables)";
print "All good";
View File
+17
View File
@@ -0,0 +1,17 @@
import standard;
import random;
var generator: opaque = createRandomGenerator(clock().hash()); //create a new generator object, from a non-determinant source
var a: int = generator.generateRandomNumber();
var b: int = generator.generateRandomNumber();
var c: int = generator.generateRandomNumber();
generator.freeRandomGenerator();
assert a != b, "random a != random b failed";
assert a != c, "random a != random c failed";
assert b != c, "random b != random c failed";
print "All good";
+94
View File
@@ -7,6 +7,100 @@ import standard;
}
//test hash
{
assert typeof "Hello world".hash() == int, "typeof \"Hello world\".hash() failed";
assert "Hello world".hash() == 994097935, "\"Hello world\".hash() failed"; //NOTE: specific value based on algorithm
}
//test abs
{
assert abs(-5) == 5, "abs(-integer) failed";
assert abs(-5.5) == 5.5, "abs(-float) failed";
assert abs(5) == 5, "abs(+integer) failed";
assert abs(5.5) == 5.5, "abs(+float) failed";
var x = -5;
assert x.abs() == 5, "var.abs() failed";
}
//test ceil
{
assert ceil(4) == 4, "ceil(int) failed";
assert ceil(4.0) == 4, "ceil(float) failed";
assert ceil(4.1) == 5, "ceil() failed";
var x = 4.1;
assert x.ceil() == 5, "var.ceil() failed";
}
//test floor
{
assert floor(4) == 4, "floor(int) failed";
assert floor(4.0) == 4, "floor(float) failed";
assert floor(4.1) == 4, "floor() failed";
var x = 4.1;
assert x.floor() == 4, "var.floor() failed";
}
//test max
{
assert max(1, 2, 3) == 3, "max() failed";
var a = 1;
var b = 2;
var c = 3;
assert max(a, b, c) == 3, "var.max() failed";
assert max(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) == 9, "max() with many args failed";
assert typeof max(1, 2, 3) == int, "typeof max() == int failed";
assert typeof max(1, 2, 3.4) == float, "typeof max() == float failed";
}
//test min
{
assert min(1, 2, 3) == 1, "min() failed";
var a = 1;
var b = 2;
var c = 3;
assert min(a, b, c) == 1, "var.min() failed";
assert min(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) == 0, "min() with many args failed";
assert typeof min(1, 2, 3) == int, "typeof min() == int failed";
assert typeof min(1, 2, 3.4) == float, "typeof min() == float failed";
}
//test round
{
assert round(4) == 4, "round(int) failed";
assert round(4.0) == 4, "round(float) failed";
assert round(4.1) == 4, "round(less than half) failed";
assert round(4.9) == 5, "round(greater than half) failed";
assert round(4.5) == 5, "round(exactly half) failed";
var x = 4.1;
assert x.round() == 4, "var.round() failed";
assert typeof round(1.0) == int, "typeof round() == int failed";
}
//test concat
{
//test array concat
+5 -53
View File
@@ -14,6 +14,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 +46,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;
@@ -94,6 +80,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,41 +112,6 @@ 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();
+11 -4
View File
@@ -10,25 +10,32 @@
int currentMemoryUsed = 0;
int maxMemoryUsed = 0;
int memoryAllocCalls = 0;
int memoryAllocFree = 0;
int memoryAllocRealloc = 0;
static void* trackerAllocator(void* pointer, size_t oldSize, size_t newSize) {
//the number of raw calls
memoryAllocCalls++;
//causes issues, so just skip out with a NO-OP
if (newSize == 0 && oldSize == 0) {
//causes issues, so just skip out with a NO-OP
return NULL;
}
memoryAllocCalls++;
//track the changes
currentMemoryUsed = currentMemoryUsed - oldSize + newSize;
maxMemoryUsed = currentMemoryUsed > maxMemoryUsed ? currentMemoryUsed : maxMemoryUsed;
if (newSize == 0) {
//the number of frees
memoryAllocFree++;
free(pointer);
return NULL;
}
//the number of reallocations
memoryAllocRealloc++;
void* mem = realloc(pointer, newSize);
if (mem == NULL) {
@@ -69,7 +76,7 @@ int main(int argc, const char* argv[]) {
Toy_freeDriveDictionary();
//report output
printf("Memory report: %d max bytes, %d calls\n", maxMemoryUsed, memoryAllocCalls);
printf("Heap Memory Report:\n\t%d max bytes\n\t%d calls to the allocator\n\t%d calls to realloc()\n\t%d calls to free()\n\t%d discrepancies\n", maxMemoryUsed, memoryAllocCalls, memoryAllocRealloc, memoryAllocFree, memoryAllocCalls - memoryAllocRealloc - memoryAllocFree);
return 0;
}