Compare commits

...

9 Commits

Author SHA1 Message Date
Kayne Ruse 17f0e4476b Caught a bug that the test cases failed to find 2023-03-17 21:58:13 +11:00
Kayne Ruse 1095e1a885 Added type casting a grouping bugfix, resolved #76 2023-03-17 20:57:47 +11:00
Kayne Ruse 2edfbbe3ef Found a compiler bug, thanks Aedan! 2023-03-17 14:01:16 +11:00
Ratstail91 4b83f1f0d6 Fixed a dumb typo 2023-03-15 06:39:19 +11:00
Kayne Ruse e2fa1cf2e8 Moved lib_runner's drive system into the core of the lang 2023-03-15 06:12:35 +11:00
Kayne Ruse a04d2c4816 Tweaked TOY_EXPORT omitting extra repl stuff 2023-03-15 04:56:26 +11:00
Kayne Ruse f2f8aed23a Added short-circuiting support to && and || 2023-03-11 17:59:09 +11:00
Kayne Ruse 68ed52b347 Tweaked precedence of binary expressions 2023-03-11 17:47:43 +11:00
Kayne Ruse 88dac53ae0 Added toy.h, thanks for the suggestion GabrielGavrilov!
Resolved #72
2023-03-10 08:41:58 +11:00
21 changed files with 349 additions and 231 deletions
+3
View File
@@ -123,6 +123,7 @@
<ClCompile Include="source\toy_builtin.c" /> <ClCompile Include="source\toy_builtin.c" />
<ClCompile Include="source\toy_common.c" /> <ClCompile Include="source\toy_common.c" />
<ClCompile Include="source\toy_compiler.c" /> <ClCompile Include="source\toy_compiler.c" />
<ClCompile Include="source\toy_drive_system.c" />
<ClCompile Include="source\toy_interpreter.c" /> <ClCompile Include="source\toy_interpreter.c" />
<ClCompile Include="source\toy_keyword_types.c" /> <ClCompile Include="source\toy_keyword_types.c" />
<ClCompile Include="source\toy_lexer.c" /> <ClCompile Include="source\toy_lexer.c" />
@@ -135,11 +136,13 @@
<ClCompile Include="source\toy_scope.c" /> <ClCompile Include="source\toy_scope.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="source\toy.h" />
<ClInclude Include="source\toy_ast_node.h" /> <ClInclude Include="source\toy_ast_node.h" />
<ClInclude Include="source\toy_builtin.h" /> <ClInclude Include="source\toy_builtin.h" />
<ClInclude Include="source\toy_common.h" /> <ClInclude Include="source\toy_common.h" />
<ClInclude Include="source\toy_compiler.h" /> <ClInclude Include="source\toy_compiler.h" />
<ClInclude Include="source\toy_console_colors.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_interpreter.h" />
<ClInclude Include="source\toy_keyword_types.h" /> <ClInclude Include="source\toy_keyword_types.h" />
<ClInclude Include="source\toy_lexer.h" /> <ClInclude Include="source\toy_lexer.h" />
+2 -2
View File
@@ -28,10 +28,10 @@ library: $(TOY_OUTDIR)
static: $(TOY_OUTDIR) static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static $(MAKE) -j8 -C source static
library-release: $(TOY_OUTDIR) library-release: clean $(TOY_OUTDIR)
$(MAKE) -j8 -C source library-release $(MAKE) -j8 -C source library-release
static-release: $(TOY_OUTDIR) static-release: clean $(TOY_OUTDIR)
$(MAKE) -j8 -C source static-release $(MAKE) -j8 -C source static-release
#utils #utils
+10 -145
View File
@@ -1,13 +1,12 @@
#include "lib_runner.h" #include "lib_runner.h"
#include "toy_memory.h" #include "toy_memory.h"
#include "toy_drive_system.h"
#include "toy_interpreter.h" #include "toy_interpreter.h"
#include "repl_tools.h" #include "repl_tools.h"
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
typedef struct Toy_Runner { typedef struct Toy_Runner {
Toy_Interpreter interpreter; Toy_Interpreter interpreter;
@@ -38,7 +37,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
return -1; return -1;
} }
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral); Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) { if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral); Toy_freeLiteral(filePathLiteral);
@@ -113,70 +112,19 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
return -1; return -1;
} }
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral)); Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library if (TOY_IS_NULL(filePathLiteral)) {
size_t driveLength = 0; Toy_freeLiteral(filePathLiteral);
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);
Toy_freeLiteral(drivePathLiteral); Toy_freeLiteral(drivePathLiteral);
return -1; return -1;
} }
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral); Toy_freeLiteral(drivePathLiteral);
//check for file extensions //use raw types - easier
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) { const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
interpreter->errorOutput("Bad binary file extension (expected .tb)\n"); size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
//check for break-out attempts
for (size_t i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
}
//load the bytecode //load the bytecode
size_t fileSize = 0; size_t fileSize = 0;
@@ -203,7 +151,8 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER); Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral); Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
TOY_FREE_ARRAY(char, filePath, realLength); //free the drive path
Toy_freeLiteral(filePathLiteral);
return 1; return 1;
} }
@@ -602,87 +551,3 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
return 0; return 0;
} }
//file system API
static Toy_LiteralDictionary Toy_driveDictionary;
void Toy_initDriveDictionary() {
Toy_initLiteralDictionary(&Toy_driveDictionary);
}
void Toy_freeDriveDictionary() {
Toy_freeLiteralDictionary(&Toy_driveDictionary);
}
Toy_LiteralDictionary* Toy_getDriveDictionary() {
return &Toy_driveDictionary;
}
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
//check for break-out attempts
for (size_t i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return result;
}
-27
View File
@@ -1,35 +1,8 @@
#pragma once #pragma once
#include "toy_common.h"
#include "toy_interpreter.h" #include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias); int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
#define TOY_OPAQUE_TAG_RUNNER 100 #define TOY_OPAQUE_TAG_RUNNER 100
//platform/compiler-specific instructions - because MSVC + Box Engine are dumber than a bag of rocks
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
#define LIB_RUNNER_API extern
#elif defined(_MSC_VER)
#ifndef LIB_RUNNER_EXPORT
#define LIB_RUNNER_API __declspec(dllimport)
#else
#define LIB_RUNNER_API __declspec(dllexport)
#endif
#else
#define LIB_RUNNER_API extern
#endif
//file system API - these need to be set by the host
LIB_RUNNER_API void Toy_initDriveDictionary();
LIB_RUNNER_API void Toy_freeDriveDictionary();
LIB_RUNNER_API Toy_LiteralDictionary* Toy_getDriveDictionary();
//file system API - for use with other libs
LIB_RUNNER_API Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+8 -18
View File
@@ -6,10 +6,7 @@
#include "toy_console_colors.h" #include "toy_console_colors.h"
#include "toy_lexer.h" #include "toy.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -106,16 +103,9 @@ void repl(const char* initialInput) {
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
Toy_initCommandLine(argc, argv); Toy_initCommandLine(argc, argv);
//lib setup (hacky - only really for this program) //setup the drive system (for filesystem access)
Toy_initDriveDictionary(); Toy_initDriveSystem();
Toy_setDrivePath("scripts", "scripts");
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);
//command line specific actions //command line specific actions
if (Toy_commandLine.error) { if (Toy_commandLine.error) {
@@ -151,7 +141,7 @@ int main(int argc, const char* argv[]) {
Toy_runSourceFile(Toy_commandLine.sourcefile); Toy_runSourceFile(Toy_commandLine.sourcefile);
//lib cleanup //lib cleanup
Toy_freeDriveDictionary(); Toy_freeDriveSystem();
return 0; return 0;
} }
@@ -161,7 +151,7 @@ int main(int argc, const char* argv[]) {
Toy_runSource(Toy_commandLine.source); Toy_runSource(Toy_commandLine.source);
//lib cleanup //lib cleanup
Toy_freeDriveDictionary(); Toy_freeDriveSystem();
return 0; return 0;
} }
@@ -207,7 +197,7 @@ int main(int argc, const char* argv[]) {
Toy_runBinaryFile(Toy_commandLine.binaryfile); Toy_runBinaryFile(Toy_commandLine.binaryfile);
//lib cleanup //lib cleanup
Toy_freeDriveDictionary(); Toy_freeDriveSystem();
return 0; return 0;
} }
@@ -228,7 +218,7 @@ int main(int argc, const char* argv[]) {
repl(initialSource); repl(initialSource);
//lib cleanup //lib cleanup
Toy_freeDriveDictionary(); Toy_freeDriveSystem();
return 0; return 0;
} }
+3 -3
View File
@@ -78,10 +78,10 @@ const unsigned char* Toy_compileString(const char* source, size_t* size) {
Toy_initParser(&parser, &lexer); Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler); 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); Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) { while(node != NULL) {
//pack up and leave //on error, pack up and leave
if (node->type == TOY_AST_NODE_ERROR) { if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node); Toy_freeASTNode(node);
Toy_freeCompiler(&compiler); Toy_freeCompiler(&compiler);
@@ -94,7 +94,7 @@ const unsigned char* Toy_compileString(const char* source, size_t* size) {
node = Toy_scanParser(&parser); node = Toy_scanParser(&parser);
} }
//get the bytecode dump //step 2 - get the bytecode dump
const unsigned char* tb = Toy_collateCompiler(&compiler, size); const unsigned char* tb = Toy_collateCompiler(&compiler, size);
//cleanup //cleanup
+4
View File
@@ -0,0 +1,4 @@
var s = "42";
var t = "69";
print int (s + t) - 1;
+68
View File
@@ -0,0 +1,68 @@
#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.
*/
#include "toy_scope.h"
#include "toy_refstring.h"
+5 -1
View File
@@ -4,7 +4,7 @@
#include <string.h> #include <string.h>
#include <assert.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") #define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(char) == 1); STATIC_ASSERT(sizeof(char) == 1);
@@ -15,6 +15,8 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
STATIC_ASSERT(sizeof(unsigned short) == 2); STATIC_ASSERT(sizeof(unsigned short) == 2);
STATIC_ASSERT(sizeof(unsigned int) == 4); STATIC_ASSERT(sizeof(unsigned int) == 4);
#ifndef TOY_EXPORT
//declare the singleton with default values //declare the singleton with default values
Toy_CommandLine Toy_commandLine = { Toy_CommandLine Toy_commandLine = {
.error = false, .error = false,
@@ -134,3 +136,5 @@ void Toy_copyrightCommandLine(int argc, const char* argv[]) {
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n"); printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
printf("3. This notice may not be removed or altered from any source distribution.\n\n"); printf("3. This notice may not be removed or altered from any source distribution.\n\n");
} }
#endif
+6 -2
View File
@@ -6,7 +6,7 @@
#define TOY_VERSION_MAJOR 1 #define TOY_VERSION_MAJOR 1
#define TOY_VERSION_MINOR 1 #define TOY_VERSION_MINOR 1
#define TOY_VERSION_PATCH 1 #define TOY_VERSION_PATCH 2
#define TOY_VERSION_BUILD __DATE__ " " __TIME__ #define TOY_VERSION_BUILD __DATE__ " " __TIME__
//platform/compiler-specific instructions //platform/compiler-specific instructions
@@ -28,7 +28,9 @@
#endif #endif
//for processing the command line arguments #ifndef TOY_EXPORT
//for processing the command line arguments in the repl
typedef struct { typedef struct {
bool error; bool error;
bool help; bool help;
@@ -51,3 +53,5 @@ TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
TOY_API void Toy_usageCommandLine(int argc, const char* argv[]); TOY_API void Toy_usageCommandLine(int argc, const char* argv[]);
TOY_API void Toy_helpCommandLine(int argc, const char* argv[]); TOY_API void Toy_helpCommandLine(int argc, const char* argv[]);
TOY_API void Toy_copyrightCommandLine(int argc, const char* argv[]); TOY_API void Toy_copyrightCommandLine(int argc, const char* argv[]);
#endif
+31 -3
View File
@@ -331,9 +331,37 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
return node->binary.opcode; 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))) { //untangle in these cases - (WTF, are you serious?)
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte if (ret != TOY_OP_EOF) {
ret = TOY_OP_EOF; //untangle in this case 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 compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
+99
View File
@@ -0,0 +1,99 @@
#include "toy_drive_system.h"
#include "toy_memory.h"
#include "toy_literal_dictionary.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//file system API
static Toy_LiteralDictionary driveDictionary;
void Toy_initDriveSystem() {
Toy_initLiteralDictionary(&driveDictionary);
}
void Toy_freeDriveSystem() {
Toy_freeLiteralDictionary(&driveDictionary);
}
TOY_API void Toy_setDrivePath(char* drive, char* path) {
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
}
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
if (!TOY_IS_STRING(pathLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
Toy_deleteRefString(filePath);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
//clean up the drive/path stuff
Toy_deleteRefString(drivePath);
Toy_deleteRefString(filePath);
Toy_deleteRefString(path);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//check for break-out attempts
for (size_t i = 0; i < fileLength - 1; i++) {
if (file[i] == '.' && file[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, file, fileLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
TOY_FREE_ARRAY(char, file, fileLength + 1);
return result;
}
+12
View File
@@ -0,0 +1,12 @@
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_interpreter.h"
//file system API - these need to be set by the host
TOY_API void Toy_initDriveSystem();
TOY_API void Toy_freeDriveSystem();
//file system API - for use with libs
TOY_API void Toy_setDrivePath(char* drive, char* path);
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+13 -6
View File
@@ -12,12 +12,17 @@
#include <string.h> #include <string.h>
static void printWrapper(const char* output) { static void printWrapper(const char* output) {
//allow for disabling of newlines in the repl
#ifndef TOY_EXPORT
if (Toy_commandLine.enablePrintNewline) { if (Toy_commandLine.enablePrintNewline) {
printf("%s\n", output); printf("%s\n", output);
} }
else { else {
printf("%s", output); printf("%s", output);
} }
#else
printf("%s\n", output);
#endif
} }
static void assertWrapper(const char* output) { static void assertWrapper(const char* output) {
@@ -1082,11 +1087,12 @@ static bool execAnd(Toy_Interpreter* interpreter) {
return false; return false;
} }
if (TOY_IS_TRUTHY(lhs) && TOY_IS_TRUTHY(rhs)) { //short-circuit support
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(true)); if (!TOY_IS_TRUTHY(lhs)) {
Toy_pushLiteralArray(&interpreter->stack, lhs);
} }
else { else {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(false)); Toy_pushLiteralArray(&interpreter->stack, rhs);
} }
Toy_freeLiteral(lhs); Toy_freeLiteral(lhs);
@@ -1115,11 +1121,12 @@ static bool execOr(Toy_Interpreter* interpreter) {
return false; return false;
} }
if (TOY_IS_TRUTHY(lhs) || TOY_IS_TRUTHY(rhs)) { //short-circuit support
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(true)); if (TOY_IS_TRUTHY(lhs)) {
Toy_pushLiteralArray(&interpreter->stack, lhs);
} }
else { else {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_BOOLEAN_LITERAL(false)); Toy_pushLiteralArray(&interpreter->stack, rhs);
} }
Toy_freeLiteral(lhs); Toy_freeLiteral(lhs);
+36 -15
View File
@@ -367,73 +367,73 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//assignment //assignment
case TOY_TOKEN_ASSIGN: { case TOY_TOKEN_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_ASSIGN; return TOY_OP_VAR_ASSIGN;
} }
case TOY_TOKEN_PLUS_ASSIGN: { case TOY_TOKEN_PLUS_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_ADDITION_ASSIGN; return TOY_OP_VAR_ADDITION_ASSIGN;
} }
case TOY_TOKEN_MINUS_ASSIGN: { case TOY_TOKEN_MINUS_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_SUBTRACTION_ASSIGN; return TOY_OP_VAR_SUBTRACTION_ASSIGN;
} }
case TOY_TOKEN_MULTIPLY_ASSIGN: { case TOY_TOKEN_MULTIPLY_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_MULTIPLICATION_ASSIGN; return TOY_OP_VAR_MULTIPLICATION_ASSIGN;
} }
case TOY_TOKEN_DIVIDE_ASSIGN: { case TOY_TOKEN_DIVIDE_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_DIVISION_ASSIGN; return TOY_OP_VAR_DIVISION_ASSIGN;
} }
case TOY_TOKEN_MODULO_ASSIGN: { case TOY_TOKEN_MODULO_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_MODULO_ASSIGN; return TOY_OP_VAR_MODULO_ASSIGN;
} }
//comparison //comparison
case TOY_TOKEN_EQUAL: { case TOY_TOKEN_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON); parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_EQUAL; return TOY_OP_COMPARE_EQUAL;
} }
case TOY_TOKEN_NOT_EQUAL: { case TOY_TOKEN_NOT_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON); parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_NOT_EQUAL; return TOY_OP_COMPARE_NOT_EQUAL;
} }
case TOY_TOKEN_LESS: { case TOY_TOKEN_LESS: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON); parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_LESS; return TOY_OP_COMPARE_LESS;
} }
case TOY_TOKEN_LESS_EQUAL: { case TOY_TOKEN_LESS_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON); parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_LESS_EQUAL; return TOY_OP_COMPARE_LESS_EQUAL;
} }
case TOY_TOKEN_GREATER: { case TOY_TOKEN_GREATER: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON); parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_GREATER; return TOY_OP_COMPARE_GREATER;
} }
case TOY_TOKEN_GREATER_EQUAL: { case TOY_TOKEN_GREATER_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON); parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_GREATER_EQUAL; return TOY_OP_COMPARE_GREATER_EQUAL;
} }
case TOY_TOKEN_AND: { case TOY_TOKEN_AND: {
parsePrecedence(parser, nodeHandle, PREC_AND); parsePrecedence(parser, nodeHandle, PREC_AND + 1);
return TOY_OP_AND; return TOY_OP_AND;
} }
case TOY_TOKEN_OR: { case TOY_TOKEN_OR: {
parsePrecedence(parser, nodeHandle, PREC_OR); parsePrecedence(parser, nodeHandle, PREC_OR + 1);
return TOY_OP_OR; return TOY_OP_OR;
} }
@@ -658,7 +658,7 @@ static Toy_Opcode castingInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
break; break;
case TOY_TOKEN_LITERAL_STRING: case TOY_TOKEN_LITERAL_STRING:
atomic(parser, nodeHandle); string(parser, nodeHandle);
break; break;
default: default:
@@ -739,6 +739,27 @@ static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
} }
static Toy_Opcode fnCall(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 advance(parser); //skip the left paren
//binary() is an infix rule - so only get the RHS of the operator //binary() is an infix rule - so only get the RHS of the operator
@@ -0,0 +1,7 @@
var s = "42";
var t = "69";
assert int (s + t) - 1 == 4268, "casting parentheses failed";
print "All good";
+3
View File
@@ -23,5 +23,8 @@ assert !false, "!false";
var c = false; var c = false;
assert !c, "!c"; assert !c, "!c";
//test multiple comparisons
assert 1 == 2 == false, "Left-accociative equality failed";
print "All good"; print "All good";
+23
View File
@@ -0,0 +1,23 @@
var days = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday"
];
var rng = 10; //for chosen at random
var index = rng % days.length();
assert index == 3, "dot modulo bugfix failed";
rng %= days.length();
assert rng == 3, "dot modulo assign bugfix failed";
print "All good";
@@ -0,0 +1,9 @@
//explicitly support && and || short circuits
assert 1 && 2 == 2, "&& short-circuit failed";
assert 1 || 2 == 1, "|| short-circuit failed";
print "All good";
+3
View File
@@ -108,12 +108,14 @@ int main() {
//run each file in tests/scripts/ //run each file in tests/scripts/
const char* filenames[] = { const char* filenames[] = {
"arithmetic.toy", "arithmetic.toy",
"casting-parentheses-bugfix.toy",
"casting.toy", "casting.toy",
"coercions.toy", "coercions.toy",
"comparisons.toy", "comparisons.toy",
"dot-and-matrix.toy", "dot-and-matrix.toy",
"dot-assignments-bugfix.toy", "dot-assignments-bugfix.toy",
"dot-chaining.toy", "dot-chaining.toy",
"dot-modulo-bugfix.toy",
"dottify-bugfix.toy", "dottify-bugfix.toy",
"functions.toy", "functions.toy",
"index-arrays.toy", "index-arrays.toy",
@@ -132,6 +134,7 @@ int main() {
"panic-within-functions.toy", "panic-within-functions.toy",
"polyfill-insert.toy", "polyfill-insert.toy",
"polyfill-remove.toy", "polyfill-remove.toy",
"short-circuiting-support.toy",
"ternary-expressions.toy", "ternary-expressions.toy",
"types.toy", "types.toy",
NULL NULL
+4 -9
View File
@@ -6,6 +6,7 @@
#include "toy_console_colors.h" #include "toy_console_colors.h"
#include "toy_memory.h" #include "toy_memory.h"
#include "toy_drive_system.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -63,15 +64,9 @@ typedef struct Payload {
int main() { int main() {
//setup the runner filesystem (hacky) //setup the runner filesystem (hacky)
Toy_initDriveDictionary(); Toy_initDriveSystem();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts")); Toy_setDrivePath("scripts", "scripts");
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
{ {
//run each file in test/scripts //run each file in test/scripts
@@ -113,7 +108,7 @@ int main() {
} }
//lib cleanup //lib cleanup
Toy_freeDriveDictionary(); Toy_freeDriveSystem();
if (!failedAsserts) { if (!failedAsserts) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);