Compare commits

..

61 Commits

Author SHA1 Message Date
Kayne Ruse cce8ae1ea3 Added disassembler, thanks @hiperiondev, bumped minor version 2023-08-14 23:06:05 +10:00
Kayne Ruse ce54912232 Removed an extra pair of SCOPE_BEGIN and SCOPE_END from function bytecode
This should reduce the memory footprint a bit
2023-08-14 22:17:33 +10:00
Kayne Ruse 23b55fc360 Fixed execFnDecl accidentally modifying the literalCache for a moment, resolved #105 2023-08-14 10:47:10 +10:00
Kayne Ruse 62fe86f99b Fixed indexing in argument lists, resolved #102 2023-08-09 02:25:07 +10:00
Ratstail91 401de578a5 Short circuitable operators are extremely loose 2023-08-06 04:53:46 +10:00
Kayne Ruse fb4258f9df Fixed broken test 2023-08-06 04:38:55 +10:00
Ratstail91 f885fdaf4c Short circuits are now functioning correctly, resolved #73 2023-08-06 04:28:02 +10:00
Kayne Ruse cfec1b6911 Added int to float coercions to function args and returns, when specified 2023-08-06 02:17:32 +10:00
Kayne Ruse a63bdaef1c Merge remote-tracking branch 'refs/remotes/origin/main' 2023-08-04 18:43:32 +10:00
Kayne Ruse 3783c94064 Allow trailing commas when writing a compound 2023-08-04 18:43:07 +10:00
Ratstail91 d292b33184 Tweaked types to stop MSVC complaining 2023-08-04 18:24:54 +10:00
Kayne Ruse 64944c24f6 Snipped some duplicate code, resolved #97 2023-08-04 14:52:01 +10:00
Kayne Ruse 604604e8bc Two opcodes weren't being used, resolved #98 2023-08-04 14:51:08 +10:00
Kayne Ruse 67e49b7477 Fixed the way an identifier was handled, resolved #99 2023-08-04 14:45:07 +10:00
Kayne Ruse 967963c9d7 Fixed a spelling mistake 2023-08-03 15:22:06 +10:00
Kayne Ruse 9b469e6eb0 Merge pull request #94 from Add00/main
Adding a math library to toy
2023-08-03 01:40:19 +10:00
Add00 f8094fa17e Added hyperbolic and additional comparisons 2023-08-02 11:25:27 -04:00
Add00 8714c56c3e Implemented feedback 2023-08-02 08:39:50 -04:00
Add00 9faaa311e0 Fixed Memory Leak 2023-08-01 17:50:20 -04:00
Add00 f5ba1181c0 Added arc versions of trigonometric functions 2023-08-01 13:41:55 -04:00
Add00 b06b2d9485 test cases and additional functions 2023-08-01 09:04:37 -04:00
Add00 e3e9ca7ece Added math library 2023-07-31 23:31:12 -04:00
Add00 81fe278c96 Added partial cos and sine implementations 2023-07-31 19:02:55 -04:00
Add00 027d093e21 Added math constants for pi and e 2023-07-31 13:56:06 -04:00
Add00 2eaf7fc71a Merge branch 'main' of https://github.com/Add00/Toy 2023-07-31 13:16:01 -04:00
Add00 c43310f316 Code clean up 2023-07-31 13:13:10 -04:00
Add00 6e07c5f2f4 Merge branch 'Ratstail91:main' into main 2023-07-31 13:11:06 -04:00
Ratstail91 7690dce3f6 Fixed casting and grouping 2023-07-31 17:26:07 +10:00
Ratstail91 1ed1993489 Whoops, lets try that again. 2023-07-31 16:55:27 +10:00
Ratstail91 9b5327b83d Merge bug 2023-07-31 16:22:04 +10:00
Ratstail91 10dbe8f8f1 Fixed casting + grouping, resolved #67 2023-07-31 16:20:45 +10:00
Ratstail91 9e4ad7a9a5 Fixed casting + grouping, resolved #67 2023-07-31 16:17:33 +10:00
Add00 5317a12383 Added radian and degree conversion functions. 2023-07-30 23:17:43 -04:00
Ratstail91 35bfa1b9f1 Tweak, these were annoying me 2023-07-31 12:06:04 +10:00
Kayne Ruse 7f692b4cb4 Renamed the about library to toy_version_info library, resovled #81 2023-07-31 11:15:10 +10:00
Kayne Ruse 0cef0abdb5 Adjusted the layout of nativeClamp() and nativeLerp(), thanks Add00! 2023-07-31 04:51:40 +10:00
Kayne Ruse 6ba42b5a9b Merge pull request #93 from Add00/main
Adding clamp and lerp functions to standard library
2023-07-31 04:12:15 +10:00
Add00 3cb62274c9 Fixed merge issue 2023-07-30 13:21:42 -04:00
Add00 60b561d809 Merge branch 'Ratstail91:main' into main 2023-07-30 13:16:57 -04:00
Add00 70b2dcd829 Added clamp and lerp functions to standard library 2023-07-30 13:15:39 -04:00
Ratstail91 0955b3ff38 Tweak to the MSVC build 2023-07-31 02:04:27 +10:00
Kayne Ruse 4137935468 Added sign() and normalize() to standard lib 2023-07-30 17:46:00 +10:00
Kayne Ruse ebeabcb9d4 Reordered some functions for clarity, caught a bug or two 2023-07-28 02:42:57 +10:00
Kayne Ruse 4d33a9473a Added tools/changing-of-the-guard.cpp, resolved #89
A present for @hyperiondev - just run this in the 'includes/' directory.

It's easier than changing things by hand.
2023-07-28 01:09:58 +10:00
Kayne Ruse 61e3cdba82 Fixed a potential linker issue 2023-07-26 08:13:05 +10:00
Ratstail91 3b7d2be87e Discovered and fixed a misplaced macro 2023-07-26 01:44:15 +10:00
Kayne Ruse fa175203c9 Tweaked docs 2023-07-26 01:08:54 +10:00
Kayne Ruse b4a3e9b42b Renamed drive system files, see #88 2023-07-26 00:53:41 +10:00
Kayne Ruse 6347778ead Moved drive system to repl, resolved #88 2023-07-26 00:46:47 +10:00
Kayne Ruse ba98624e82 Fixed C-API function name, resolved #87, thanks @hiperiondev 2023-07-26 00:22:45 +10:00
Ratstail91 24ce965e76 Smoothed out building on Windows 2023-07-23 23:57:48 +10:00
Kayne Ruse 29c130135c Tweak 2023-07-23 18:34:52 +10:00
Kayne Ruse 01eca37560 Comment tweak 2023-07-22 19:14:33 +10:00
Kayne Ruse 8e5ec7d847 Tweaked docs in repl_tools.h and toy.h 2023-07-22 19:11:45 +10:00
Kayne Ruse 9ed6383630 Wrote tests for the drive system, resolved #75 2023-07-21 05:20:59 +10:00
Kayne Ruse 1ec0f63f76 Removed unneeded safties from the libs
I missed removing these before, and I haven't needed these in a long time.
2023-07-21 04:25:36 +10:00
Kayne Ruse d2341ae227 Bumped version number 2023-07-21 03:08:06 +10:00
Kayne Ruse 3782f2aaaa Comment tweak 2023-07-21 02:59:07 +10:00
Kayne Ruse b636ab9e31 Added a new issue template 2023-07-21 02:41:45 +10:00
Kayne Ruse cdfe17ad53 Added mecha-style comment docs to a bunch of headers 2023-07-20 19:44:40 +10:00
Kayne Ruse 3d7d1179c9 Added mecha tool 2023-07-19 18:09:18 +10:00
70 changed files with 4732 additions and 849 deletions
+10
View File
@@ -0,0 +1,10 @@
---
name: Question
about: Ask a Question
labels: question
---
### How can I help?
I'm always here to help with any inquiries you have regarding Toy and its related projects.
+1
View File
@@ -28,6 +28,7 @@ bin/
*.stackdump
*.tb
*.filters
[Dd]ocs/
#Shell files
*.bat
+25 -21
View File
@@ -71,12 +71,12 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IgnoreImportLibrary>false</IgnoreImportLibrary>
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<OutDir>$(SolutionDir)out\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<OutDir>$(SolutionDir)out\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -110,7 +110,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Link>
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(SolutionDir)out</AdditionalLibraryDirectories>
</Link>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -125,30 +125,34 @@
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(SolutionDir)out</AdditionalLibraryDirectories>
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</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" />
<ClCompile Include="repl\repl_tools.c" />
</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>
<ItemGroup>
<ClCompile Include="repl\drive_system.c" />
<ClCompile Include="repl\lib_math.c" />
<ClCompile Include="repl\lib_random.c" />
<ClCompile Include="repl\lib_runner.c" />
<ClCompile Include="repl\lib_standard.c" />
<ClCompile Include="repl\lib_toy_version_info.c" />
<ClCompile Include="repl\repl_main.c" />
<ClCompile Include="repl\repl_tools.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="repl\drive_system.h" />
<ClInclude Include="repl\lib_math.h" />
<ClInclude Include="repl\lib_random.h" />
<ClInclude Include="repl\lib_runner.h" />
<ClInclude Include="repl\lib_standard.h" />
<ClInclude Include="repl\lib_toy_version_info.h" />
<ClInclude Include="repl\repl_tools.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
+26 -6
View File
@@ -70,12 +70,14 @@
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<OutDir>$(SolutionDir)out\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<OutDir>$(SolutionDir)out\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -111,19 +113,38 @@
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Link>
<OutputFile>$(Outdir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Bscmake>
<OutputFile>$(Platform)\$(Configuration)\$(TargetName).bsc</OutputFile>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Link>
<OutputFile>$(Outdir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Bscmake>
<OutputFile>$(Platform)\$(Configuration)\$(TargetName).bsc</OutputFile>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="source\toy_ast_node.c" />
<ClCompile Include="source\toy_builtin.c" />
<ClCompile Include="source\toy_common.c" />
<ClCompile Include="source\toy_compiler.c" />
<ClCompile Include="source\toy_drive_system.c" />
<ClCompile Include="source\toy_interpreter.c" />
<ClCompile Include="source\toy_keyword_types.c" />
<ClCompile Include="source\toy_lexer.c" />
@@ -143,7 +164,6 @@
<ClInclude Include="source\toy_common.h" />
<ClInclude Include="source\toy_compiler.h" />
<ClInclude Include="source\toy_console_colors.h" />
<ClInclude Include="source\toy_drive_system.h" />
<ClInclude Include="source\toy_interpreter.h" />
<ClInclude Include="source\toy_keyword_types.h" />
<ClInclude Include="source\toy_lexer.h" />
+19
View File
@@ -51,6 +51,25 @@ $(TOY_OUTDIR):
install-tools:
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
#utils
build-mecha: $(TOY_OUTDIR)
g++ -o $(TOY_OUTDIR)/mecha tools/mecha.cpp
build-docs: build-mecha
$(TOY_OUTDIR)/mecha $(wildcard source/*.h)
$(TOY_OUTDIR)/mecha $(wildcard repl/*.h)
docs:
mkdir docs
move-docs: docs
mv -u $(wildcard source/*.md) docs
mv -u $(wildcard repl/*.md) docs
documentation:
$(MAKE) build-docs
$(MAKE) move-docs
.PHONY: clean
clean:
@@ -1,4 +1,4 @@
#include "toy_drive_system.h"
#include "drive_system.h"
#include "toy_memory.h"
#include "toy_literal_dictionary.h"
@@ -18,7 +18,7 @@ void Toy_freeDriveSystem() {
Toy_freeLiteralDictionary(&driveDictionary);
}
TOY_API void Toy_setDrivePath(char* drive, char* path) {
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));
+76
View File
@@ -0,0 +1,76 @@
#pragma once
/*!
# drive_system.h
When accessing the file system through Toy (such as with the runner library), it's best practice to utilize the drive system - this system (tries to) prevent malicious accessing of files outside of the designated folders. It does this by causing an error when a script tries to access a parent directory.
To use the drive system, first you must designate specific folders which can be accessed, like so:
```c
#include "drive_system.h"
int main(int argc, char* argv[]) {
//the drive system uses a LiteralDictionary, which must be initialized with this
Toy_initDriveSystem();
Toy_setDrivePath("scripts", "assets/scripts");
Toy_setDrivePath("sprites", "assets/sprites");
Toy_setDrivePath("fonts", "assets/fonts");
//TODO: do you stuff here
//clean up the drive dictionary when you're done
Toy_freeDriveSystem();
return 0;
}
```
This utility is intended mainly for libraries to use - as such, the core of Toy does not utilize it.
### Implementation Details
The drive system uses a Toy's Dictionary structure to store the mappings between keys and values - this dictionary object is a static global which persists for the lifetime of the program.
!*/
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_interpreter.h"
/*!
## Defined Functions
!*/
/*!
### void Toy_initDriveSystem()
This function initializes the drive system.
!*/
TOY_API void Toy_initDriveSystem();
/*!
### void Toy_freeDriveSystem()
This function cleans up after the drive system is no longer needed.
!*/
TOY_API void Toy_freeDriveSystem();
/*!
### void Toy_setDrivePath(char* drive, char* path)
This function sets a key-value pair in the drive system. It uses C strings, since its intended to be called directly from `main()`.
!*/
TOY_API void Toy_setDrivePath(char* drive, char* path);
/*!
### Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral)
This function, when given a string literal of the correct format, will return a new string literal containing the relative filepath to a specified file.
The correct format is `drive:/path/to/filename`, where `drive` is a drive that was specified with `Toy_setDrivePath()`.
On failure, this function returns a null literal.
!*/
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+1152
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookMath(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+1 -16
View File
@@ -30,11 +30,6 @@ static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_Literal
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);
@@ -70,11 +65,6 @@ static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralA
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;
@@ -111,11 +101,6 @@ static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralAr
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;
@@ -148,7 +133,7 @@ int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
//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)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
+2 -2
View File
@@ -2,6 +2,6 @@
#include "toy_interpreter.h"
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
#define TOY_OPAQUE_TAG_RANDOM 200
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+2 -44
View File
@@ -1,10 +1,10 @@
#include "lib_runner.h"
#include "toy_memory.h"
#include "toy_drive_system.h"
#include "toy_interpreter.h"
#include "repl_tools.h"
#include "drive_system.h"
#include <stdlib.h>
@@ -32,11 +32,6 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
Toy_freeLiteral(drivePathLiteralIdn);
}
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
Toy_freeLiteral(drivePathLiteral);
return -1;
}
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
@@ -107,11 +102,6 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
Toy_freeLiteral(drivePathLiteralIdn);
}
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
Toy_freeLiteral(drivePathLiteral);
return -1;
}
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
@@ -172,11 +162,6 @@ static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in runScript\n");
return -1;
@@ -224,12 +209,6 @@ static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in getScriptVar\n");
return -1;
@@ -302,12 +281,6 @@ static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in callScriptFn\n");
return -1;
@@ -377,11 +350,6 @@ static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arg
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in resetScript\n");
return -1;
@@ -418,11 +386,6 @@ static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in freeScript\n");
return -1;
@@ -457,11 +420,6 @@ static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray
Toy_freeLiteral(runnerIdn);
}
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
Toy_freeLiteral(runnerLiteral);
return -1;
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in checkScriptDirty\n");
return -1;
@@ -504,7 +462,7 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
//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)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
+1 -2
View File
@@ -2,7 +2,6 @@
#include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
#define TOY_OPAQUE_TAG_RUNNER 100
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+262 -156
View File
@@ -47,11 +47,6 @@ static int nativeHash(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
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);
@@ -77,11 +72,6 @@ static int nativeAbs(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
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);
@@ -120,11 +110,6 @@ static int nativeCeil(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
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);
@@ -164,11 +149,6 @@ static int nativeFloor(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
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);
@@ -208,11 +188,6 @@ static int nativeMax(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
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);
@@ -227,11 +202,11 @@ static int nativeMax(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
//cooerce if needed
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
resultLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(resultLiteral) );
resultLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(resultLiteral) );
}
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
selfLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(selfLiteral) );
selfLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(selfLiteral) );
}
//compare
@@ -269,11 +244,6 @@ static int nativeMin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
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);
@@ -288,11 +258,11 @@ static int nativeMin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
//cooerce if needed
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
resultLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(resultLiteral) );
resultLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(resultLiteral) );
}
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
selfLiteral = TOY_TO_FLOAT_LITERAL( TOY_AS_INTEGER(selfLiteral) );
selfLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(selfLiteral) );
}
//compare
@@ -330,11 +300,6 @@ static int nativeRound(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
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);
@@ -365,6 +330,259 @@ static int nativeRound(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
return 1;
}
static int nativeSign(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to sign\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_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to sign\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
if (TOY_IS_INTEGER(selfLiteral)) {
if (TOY_AS_INTEGER(selfLiteral) < 0) {
resultLiteral = TOY_TO_INTEGER_LITERAL(-1);
}
else {
resultLiteral = TOY_TO_INTEGER_LITERAL(1);
}
}
if (TOY_IS_FLOAT(selfLiteral)) {
if (TOY_AS_FLOAT(selfLiteral) < 0) {
resultLiteral = TOY_TO_INTEGER_LITERAL(-1);
}
else {
resultLiteral = TOY_TO_INTEGER_LITERAL(1);
}
}
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeNormalize(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//NOTE: this is identical to `sign`, except it returns 0 when the argument is 0.
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to normalize\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_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to normalize\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
if (TOY_IS_INTEGER(selfLiteral)) {
if (TOY_AS_INTEGER(selfLiteral) < 0) {
resultLiteral = TOY_TO_INTEGER_LITERAL(-1);
}
else if (TOY_AS_INTEGER(selfLiteral) > 0) {
resultLiteral = TOY_TO_INTEGER_LITERAL(1);
}
else {
resultLiteral = TOY_TO_INTEGER_LITERAL(0);
}
}
if (TOY_IS_FLOAT(selfLiteral)) {
if (TOY_AS_FLOAT(selfLiteral) < 0) {
resultLiteral = TOY_TO_INTEGER_LITERAL(-1);
}
else if (TOY_AS_FLOAT(selfLiteral) > 0) {
resultLiteral = TOY_TO_INTEGER_LITERAL(1);
}
else {
resultLiteral = TOY_TO_INTEGER_LITERAL(0);
}
}
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeClamp(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 3) {
interpreter->errorOutput("Incorrect number of arguments to clamp\n");
return -1;
}
//get the arguments
Toy_Literal maxLiteral = Toy_popLiteralArray(arguments);
Toy_Literal minLiteral = Toy_popLiteralArray(arguments);
Toy_Literal valueLiteral = Toy_popLiteralArray(arguments);
//parse the arguments (if they're identifiers)
Toy_Literal valueLiteralIdn = valueLiteral;
if (TOY_IS_IDENTIFIER(valueLiteral) && Toy_parseIdentifierToValue(interpreter, &valueLiteral)) {
Toy_freeLiteral(valueLiteralIdn);
}
Toy_Literal minLiteralIdn = minLiteral;
if (TOY_IS_IDENTIFIER(minLiteral) && Toy_parseIdentifierToValue(interpreter, &minLiteral)) {
Toy_freeLiteral(minLiteralIdn);
}
Toy_Literal maxLiteralIdn = maxLiteral;
if (TOY_IS_IDENTIFIER(maxLiteral) && Toy_parseIdentifierToValue(interpreter, &maxLiteral)) {
Toy_freeLiteral(maxLiteralIdn);
}
//check the types
if (!(TOY_IS_INTEGER(valueLiteral) || TOY_IS_FLOAT(valueLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to clamp\n");
Toy_freeLiteral(valueLiteral);
Toy_freeLiteral(minLiteral);
Toy_freeLiteral(maxLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(minLiteral) || TOY_IS_FLOAT(minLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to clamp\n");
Toy_freeLiteral(valueLiteral);
Toy_freeLiteral(minLiteral);
Toy_freeLiteral(maxLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(maxLiteral) || TOY_IS_FLOAT(maxLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to clamp\n");
Toy_freeLiteral(valueLiteral);
Toy_freeLiteral(minLiteral);
Toy_freeLiteral(maxLiteral);
return -1;
}
// cast ints to floats to handle all types of numbers
float value = TOY_IS_INTEGER(valueLiteral)? TOY_AS_INTEGER(valueLiteral) : TOY_AS_FLOAT(valueLiteral);
float min = TOY_IS_INTEGER(minLiteral)? TOY_AS_INTEGER(minLiteral) : TOY_AS_FLOAT(minLiteral);
float max = TOY_IS_INTEGER(maxLiteral)? TOY_AS_INTEGER(maxLiteral) : TOY_AS_FLOAT(maxLiteral);
//determine which literal to return (this way, we retain the original type)
if (min > value) {
Toy_pushLiteralArray(&interpreter->stack, minLiteral);
}
else if (max < value) {
Toy_pushLiteralArray(&interpreter->stack, maxLiteral);
}
else {
Toy_pushLiteralArray(&interpreter->stack, valueLiteral);
}
Toy_freeLiteral(valueLiteral);
Toy_freeLiteral(minLiteral);
Toy_freeLiteral(maxLiteral);
return 1;
}
static int nativeLerp(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 3) {
interpreter->errorOutput("Incorrect number of arguments to lerp\n");
return -1;
}
//get the arguments
Toy_Literal amountLiteral = Toy_popLiteralArray(arguments);
Toy_Literal endLiteral = Toy_popLiteralArray(arguments);
Toy_Literal startLiteral = Toy_popLiteralArray(arguments);
//parse the arguments (if they're identifiers)
Toy_Literal startLiteralIdn = startLiteral;
if (TOY_IS_IDENTIFIER(startLiteral) && Toy_parseIdentifierToValue(interpreter, &startLiteral)) {
Toy_freeLiteral(startLiteralIdn);
}
Toy_Literal endLiteralIdn = endLiteral;
if (TOY_IS_IDENTIFIER(endLiteral) && Toy_parseIdentifierToValue(interpreter, &endLiteral)) {
Toy_freeLiteral(endLiteralIdn);
}
Toy_Literal amountLiteralIdn = amountLiteral;
if (TOY_IS_IDENTIFIER(amountLiteral) && Toy_parseIdentifierToValue(interpreter, &amountLiteral)) {
Toy_freeLiteral(amountLiteralIdn);
}
//check the argument types
if (!(TOY_IS_INTEGER(startLiteral) || TOY_IS_FLOAT(startLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to lerp\n");
Toy_freeLiteral(startLiteral);
Toy_freeLiteral(endLiteral);
Toy_freeLiteral(amountLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(endLiteral) || TOY_IS_FLOAT(endLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to lerp\n");
Toy_freeLiteral(startLiteral);
Toy_freeLiteral(endLiteral);
Toy_freeLiteral(amountLiteral);
return -1;
}
if (!(TOY_IS_INTEGER(amountLiteral) || TOY_IS_FLOAT(amountLiteral))) {
interpreter->errorOutput("Incorrect argument type passed to lerp\n");
Toy_freeLiteral(startLiteral);
Toy_freeLiteral(endLiteral);
Toy_freeLiteral(amountLiteral);
return -1;
}
// cast ints to floats to handle all types of numbers
float start = TOY_IS_INTEGER(startLiteral)? TOY_AS_INTEGER(startLiteral) : TOY_AS_FLOAT(startLiteral);
float end = TOY_IS_INTEGER(endLiteral)? TOY_AS_INTEGER(endLiteral) : TOY_AS_FLOAT(endLiteral);
float amount = TOY_IS_INTEGER(amountLiteral)? TOY_AS_INTEGER(amountLiteral) : TOY_AS_FLOAT(amountLiteral);
//calculate the result
float result = start + amount * (end - start);
//return the result
Toy_Literal resultLiteral = TOY_TO_FLOAT_LITERAL(result);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(startLiteral);
Toy_freeLiteral(endLiteral);
Toy_freeLiteral(amountLiteral);
return 1;
}
static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
@@ -387,12 +605,6 @@ static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
Toy_freeLiteral(otherLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(otherLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(otherLiteral);
return -1;
}
//for each self type
if (TOY_IS_ARRAY(selfLiteral)) {
if (!TOY_IS_ARRAY(otherLiteral)) {
@@ -503,12 +715,6 @@ static int nativeContainsKey(Toy_Interpreter* interpreter, Toy_LiteralArray* arg
Toy_freeLiteral(keyLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(keyLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(keyLiteral);
return -1;
}
//check type
if (!(/* TOY_IS_ARRAY(selfLiteral) || */ TOY_IS_DICTIONARY(selfLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to containsKey\n");
@@ -554,12 +760,6 @@ static int nativeContainsValue(Toy_Interpreter* interpreter, Toy_LiteralArray* a
Toy_freeLiteral(valueLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(valueLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to containsValue\n");
@@ -630,12 +830,6 @@ static int nativeEvery(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to every\n");
@@ -752,12 +946,6 @@ static int nativeFilter(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to filter\n");
@@ -872,12 +1060,6 @@ static int nativeForEach(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to forEach\n");
@@ -950,11 +1132,6 @@ static int nativeGetKeys(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//check type
if (!TOY_IS_DICTIONARY(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to getKeys\n");
@@ -1000,11 +1177,6 @@ static int nativeGetValues(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//check type
if (!TOY_IS_DICTIONARY(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to getValues\n");
@@ -1057,12 +1229,6 @@ static int nativeIndexOf(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(valueLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(valueLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
//check type
if (!TOY_IS_ARRAY(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to indexOf\n");
@@ -1110,12 +1276,6 @@ static int nativeMap(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to map\n");
@@ -1225,13 +1385,6 @@ static int nativeReduce(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(defaultLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(defaultLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to reduce\n");
@@ -1327,12 +1480,6 @@ static int nativeSome(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!( TOY_IS_ARRAY(selfLiteral) || TOY_IS_DICTIONARY(selfLiteral) ) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to some\n");
@@ -1502,12 +1649,6 @@ static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(fnLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(fnLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(fnLiteral);
return -1;
}
//check type
if (!TOY_IS_ARRAY(selfLiteral) || !( TOY_IS_FUNCTION(fnLiteral) || TOY_IS_FUNCTION_NATIVE(fnLiteral) )) {
interpreter->errorOutput("Incorrect argument type passed to sort\n");
@@ -1570,11 +1711,6 @@ static int nativeToLower(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to toLower\n");
Toy_freeLiteral(selfLiteral);
@@ -1636,17 +1772,6 @@ static int nativeToString(Toy_Interpreter* interpreter, Toy_LiteralArray* argume
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//BUGFIX: probably an undefined variable
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
//print it to a custom function
Toy_printLiteralCustom(selfLiteral, toStringUtil);
@@ -1680,11 +1805,6 @@ static int nativeToUpper(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral)) {
Toy_freeLiteral(selfLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to toUpper\n");
Toy_freeLiteral(selfLiteral);
@@ -1745,12 +1865,6 @@ static int nativeTrim(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(trimCharsLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(trimCharsLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to trim\n");
Toy_freeLiteral(trimCharsLiteral);
@@ -1862,12 +1976,6 @@ static int nativeTrimBegin(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(trimCharsLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(trimCharsLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to trimBegin\n");
Toy_freeLiteral(trimCharsLiteral);
@@ -1956,12 +2064,6 @@ static int nativeTrimEnd(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
Toy_freeLiteral(selfLiteralIdn);
}
if (TOY_IS_IDENTIFIER(selfLiteral) || TOY_IS_IDENTIFIER(trimCharsLiteral)) {
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(trimCharsLiteral);
return -1;
}
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to trimEnd\n");
Toy_freeLiteral(trimCharsLiteral);
@@ -2042,6 +2144,10 @@ int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L
{"max", nativeMax},
{"min", nativeMin},
{"round", nativeRound},
{"sign", nativeSign},
{"normalize", nativeNormalize},
{"clamp", nativeClamp},
{"lerp", nativeLerp},
//compound utils
{"concat", nativeConcat}, //array, dictionary, string
@@ -2069,7 +2175,7 @@ int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L
//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)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
-1
View File
@@ -3,4 +3,3 @@
#include "toy_interpreter.h"
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
@@ -1,23 +1,23 @@
#include "lib_about.h"
#include "lib_toy_version_info.h"
#include "toy_memory.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//the about keys
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//the info keys
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
//the about identifiers
//the info identifiers
Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
//the about values
//the info values
Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
@@ -27,7 +27,7 @@ int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
//store as an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
@@ -83,11 +83,11 @@ int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
//store globally
else {
//make sure the names aren't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
Toy_isDeclaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
Toy_isDeclaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
Toy_isDeclaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
Toy_isDeclaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+1 -1
View File
@@ -2,7 +2,7 @@ CC=gcc
IDIR+=. ../source
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=-ltoy
LIBS+=-ltoy -lm
ODIR = obj
SRC = $(wildcard *.c)
+5 -2
View File
@@ -1,8 +1,10 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "drive_system.h"
#include "lib_toy_version_info.h"
#include "lib_standard.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "lib_math.h"
#include "toy_console_colors.h"
@@ -25,10 +27,11 @@ void repl(const char* initialInput) {
Toy_initInterpreter(&interpreter);
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
for(;;) {
if (!initialInput) {
+5 -3
View File
@@ -1,8 +1,9 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_toy_version_info.h"
#include "lib_standard.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "lib_math.h"
#include "toy_console_colors.h"
@@ -111,10 +112,11 @@ void Toy_runBinary(const unsigned char* tb, size_t size) {
Toy_initInterpreter(&interpreter);
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
Toy_runInterpreter(&interpreter, tb, (int)size);
Toy_freeInterpreter(&interpreter);
@@ -159,7 +161,7 @@ static unsigned char readByte(const unsigned char* tb, int* count) {
static const char* readString(const unsigned char* tb, int* count) {
const unsigned char* ret = tb + *count;
*count += strlen((char*)ret) + 1; //+1 for null character
*count += (int)strlen((char*)ret) + 1; //+1 for null character
return (const char*)ret;
}
+69
View File
@@ -1,15 +1,84 @@
#pragma once
/*!
# repl_tools.h
This header provides a number of tools for compiling and running Toy, and is used primarily by the repl. However, it can also be modified and used by any host program with a little effort.
This is not a core part of Toy or a library, and as such `repl_tools.h` and `repl_tools.c` can both be found in the `repl/` folder.
!*/
#include "toy_common.h"
/*!
## Defined Functions
!*/
/*!
### const char* Toy_readFile(const char* path, size_t* fileSize)
This function reads in a file, and returns it as a constant buffer. It also sets the variable pointed to by `fileSize` to the size of the given buffer.
On error, this function returns `NULL`.
!*/
const unsigned char* Toy_readFile(const char* path, size_t* fileSize);
/*!
### int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size)
This function writes the buffer pointed to by `bytes` to a file specified by `path`. The buffer's size should be specified by `size`.
On error, this function returns a non-zero value.
!*/
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
/*!
### const unsigned char* Toy_compileString(const char* source, size_t* size)
This function takes a cstring of Toy source code, and returns a compiled buffer based on that source code. The variable pointed to by `size` is set to the size of the bytecode.
On error, this function returns `NULL`.
!*/
const unsigned char* Toy_compileString(const char* source, size_t* size);
/*!
### void Toy_runBinary(const unsigned char* tb, size_t size)
This function takes a bytecode array of `size` size, and executes it. The libraries available to the code are currently:
* lib_toy_version_info
* lib_standard
* lib_random
* lib_runner
!*/
void Toy_runBinary(const unsigned char* tb, size_t size);
/*!
### void Toy_runBinaryFile(const char* fname)
This function loads in the binary file specified by `fname`, and passes it to `Toy_runBinary()`.
!*/
void Toy_runBinaryFile(const char* fname);
/*!
### void Toy_runSource(const char* source)
This function compiles the source with `Toy_compileString()`, and passes it to `Toy_runBinary()`.
!*/
void Toy_runSource(const char* source);
/*!
### void Toy_runSourceFile(const char* fname)
This function loads in the file specified by `fname`, compiles it, and passes it to `Toy_runBinary()`.
!*/
void Toy_runSourceFile(const char* fname);
/*!
### void Toy_parseBinaryFileHeader(const char* fname)
This function parses the header information stored within the bytecode file `fname`.
This is only used for debugging and validation purposes.
!*/
void Toy_parseBinaryFileHeader(const char* fname);
+12
View File
@@ -0,0 +1,12 @@
import standard;
var array = [42];
var result = null;
//problematic line
result = max(0, array[0]);
assert result == 42, "Indexing in argument list failed";
print "All good";
+3 -3
View File
@@ -11,14 +11,14 @@ OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUTNAME=toy
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Linux)
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
CFLAGS += -fPIC
else ifeq ($(OS),Windows_NT)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Darwin)
LIBLINE = $(OBJ)
+43 -26
View File
@@ -1,69 +1,86 @@
#pragma once
/* toy.h - A Toy Programming Language
/*!
# 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.
Otherwise, this header may help learn how Toy works internally.
!*/
*/
/*!
## Utilities
/* utilities - these define a bunch of useful macros based on platform.
These headers define a bunch of useful macros, based on what platform you build for.
The most important one is `TOY_API`, which highlights functions intended for the end user.
The most important macro is `TOY_API`, which specifies functions intended for the end user.
*/
* [toy_common.h](toy_common_h.md)
* [toy_console_colors.h](toy_console_colors_h.md)
* [toy_memory.h](toy_memory_h.md)
!*/
#include "toy_common.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_drive_system.h"
/* core pipeline - from source to execution
/*!
## Core Pipeline
Each step is as follows:
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.
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.
*/
* [toy_lexer.h](toy_lexer_h.md)
* [toy_parser.h](toy_parser_h.md)
* [toy_compiler.h](toy_compiler_h.md)
* [toy_interpreter.h](toy_interpreter_h.md)
!*/
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
/* building block structures - the basic units of operation
/*!
## Building Block Structures
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.
*/
Literal arrays are contiguous 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.
* [toy_literal.h](toy_literal_h.md)
* [toy_literal_array.h](toy_literal_array_h.md)
* [toy_literal_dictionary.h](toy_literal_dictionary_h.md)
!*/
#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.
/*!
## Other Components
`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.
You probably won't use these directly, but they're a good learning opportunity.
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and
faster to copy and move. In reality, since strings are considered immutable, multiple variables can point
to the same string to save memory, and you can just create a new one of these vars pointing to the original
rather than copying entirely for a speed boost. This module has it's own memory allocator system that is
plugged into the main memory allocator.
`Toy_Scope` holds the variables of a specific scope within Toy - be it a script, a function, a block, etc. Scopes are also where the type system lives at runtime. They use identifier literals as keys, exclusively.
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and faster to copy and move. In reality, since strings are considered immutable, multiple variables can point to the same string to save memory, and you can just create a new one of these vars pointing to the original rather than copying entirely for a speed boost. This module has it's own memory allocator system that is plugged into the main memory allocator.
`Toy_RefFunction` acts similarly to `Toy_RefString`, but instead operates on function bytecode.
*/
* [toy_scope.h](toy_scope_h.md)
* [toy_refstring.h](toy_refstring_h.md)
* [toy_reffunction.h](toy_reffunction_h.md)
!*/
#include "toy_scope.h"
#include "toy_refstring.h"
+30
View File
@@ -124,6 +124,16 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
//NO-OP
break;
case TOY_AST_NODE_AND:
Toy_freeASTNode(node->pathAnd.left);
Toy_freeASTNode(node->pathAnd.right);
break;
case TOY_AST_NODE_OR:
Toy_freeASTNode(node->pathOr.left);
Toy_freeASTNode(node->pathOr.right);
break;
case TOY_AST_NODE_PREFIX_INCREMENT:
Toy_freeLiteral(node->prefixIncrement.identifier);
break;
@@ -348,6 +358,26 @@ void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
*nodeHandle = tmp;
}
void Toy_emitASTNodeAnd(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_AND;
tmp->binary.left = *nodeHandle;
tmp->binary.right = rhs;
*nodeHandle = tmp;
}
void Toy_emitASTNodeOr(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_OR;
tmp->binary.left = *nodeHandle;
tmp->binary.right = rhs;
*nodeHandle = tmp;
}
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
+23
View File
@@ -29,6 +29,8 @@ typedef enum Toy_ASTNodeType {
TOY_AST_NODE_FOR, //for control flow
TOY_AST_NODE_BREAK, //for control flow
TOY_AST_NODE_CONTINUE, //for control flow
TOY_AST_NODE_AND, //for control flow
TOY_AST_NODE_OR, //for control flow
TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
@@ -204,6 +206,24 @@ typedef struct Toy_NodeContinue {
Toy_ASTNodeType type;
} Toy_NodeContinue;
//and operator
void Toy_emitASTNodeAnd(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs); //handled node becomes lhs
typedef struct Toy_NodeAnd {
Toy_ASTNodeType type;
Toy_ASTNode* left;
Toy_ASTNode* right;
} Toy_NodeAnd;
//or operator
void Toy_emitASTNodeOr(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs); //handled node becomes lhs
typedef struct Toy_NodeOr {
Toy_ASTNodeType type;
Toy_ASTNode* left;
Toy_ASTNode* right;
} Toy_NodeOr;
//pre-post increment/decrement
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
@@ -263,6 +283,8 @@ union Toy_private_node {
Toy_NodeFor pathFor;
Toy_NodeBreak pathBreak;
Toy_NodeContinue pathContinue;
Toy_NodeAnd pathAnd;
Toy_NodeOr pathOr;
Toy_NodePrefixIncrement prefixIncrement;
Toy_NodePrefixDecrement prefixDecrement;
Toy_NodePostfixIncrement postfixIncrement;
@@ -270,4 +292,5 @@ union Toy_private_node {
Toy_NodeImport import;
};
//see toy_parser.h for more documentation on this function
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
+58 -5
View File
@@ -1,15 +1,23 @@
#pragma once
/*!
# toy_common.h
This file is generally included in most header files within Toy, as it is where the TOY_API macro is defined. It also has some utilities intended for use only by the repl.
## Defined Macros
!*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define TOY_VERSION_MAJOR 1
#define TOY_VERSION_MINOR 1
#define TOY_VERSION_PATCH 6
#define TOY_VERSION_BUILD Toy_private_version_build()
/*!
### TOY_API
This definition of this macro is platform-dependant, and used to enable cross-platform compilation of shared and static libraries.
!*/
//platform/compiler-specific instructions
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
#define TOY_API extern
@@ -28,8 +36,53 @@
#endif
/*!
### TOY_VERSION_MAJOR
The current major version of Toy. This value is embedded into the bytecode, and the interpreter will refuse to run bytecode with a major version that does not match its own version.
This value MUST fit into an unsigned char.
!*/
#define TOY_VERSION_MAJOR 1
/*!
### TOY_VERSION_MINOR
The current minor version of Toy. This value is embedded into the bytecode, and the interpreter will refuse to run bytecode with a minor version that is greater than its own minor version.
This value MUST fit into an unsigned char.
!*/
#define TOY_VERSION_MINOR 3
/*!
### TOY_VERSION_PATCH
The current patch version of Toy. This value is embedded into the bytecode.
This value MUST fit into an unsigned char.
!*/
#define TOY_VERSION_PATCH 0
/*!
### TOY_VERSION_BUILD
The current build version of Toy. This value is embedded into the bytecode.
This evaluates to a c-string, which contains build information such as compilation date and time of the interpreter. When in verbose mode, the compiler will display a warning if the build version of the bytecode does not match the build version of the interpreter.
This macro may also be used to store additonal information about forks of the Toy codebase.
!*/
#define TOY_VERSION_BUILD Toy_private_version_build()
TOY_API const char* Toy_private_version_build();
/*
The following code is intended only for use within the repl.
*/
//for processing the command line arguments in the repl
typedef struct {
bool error;
+71 -15
View File
@@ -48,7 +48,7 @@ static int writeLiteralTypeToCache(Toy_LiteralArray* literalCache, Toy_Literal l
}
//optimisation: check if exactly this literal array exists
int index = Toy_findLiteralIndex(literalCache, literal);
int index = Toy_private_findLiteralIndex(literalCache, literal);
if (index < 0) {
index = Toy_pushLiteralArray(literalCache, literal);
}
@@ -74,7 +74,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
switch(node->compound.nodes[i].pair.left->type) {
case TOY_AST_NODE_LITERAL: {
//keys are literals
int key = Toy_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
int key = Toy_private_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
if (key < 0) {
key = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
}
@@ -103,7 +103,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
switch(node->compound.nodes[i].pair.right->type) {
case TOY_AST_NODE_LITERAL: {
//values are literals
int val = Toy_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
int val = Toy_private_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
if (val < 0) {
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
}
@@ -142,7 +142,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
switch(node->compound.nodes[i].type) {
case TOY_AST_NODE_LITERAL: {
//values
int val = Toy_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
int val = Toy_private_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
if (val < 0) {
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
}
@@ -230,7 +230,7 @@ static int writeNodeCollectionToCache(Toy_Compiler* compiler, Toy_ASTNode* node)
static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
//get the index
int index = Toy_findLiteralIndex(&compiler->literalCache, literal);
int index = Toy_private_findLiteralIndex(&compiler->literalCache, literal);
if (index < 0) {
if (TOY_IS_TYPE(literal)) {
@@ -318,6 +318,12 @@ bool checkNodeInTree(Toy_ASTNode* tree, Toy_ASTNode* node) {
case TOY_AST_NODE_FOR:
return checkNodeInTree(tree->pathFor.preClause, node) || checkNodeInTree(tree->pathFor.condition, node) || checkNodeInTree(tree->pathFor.postClause, node) || checkNodeInTree(tree->pathFor.thenPath, node);
case TOY_AST_NODE_AND:
return checkNodeInTree(tree->pathAnd.left, node) || checkNodeInTree(tree->pathAnd.right, node);
case TOY_AST_NODE_OR:
return checkNodeInTree(tree->pathOr.left, node) || checkNodeInTree(tree->pathOr.right, node);
case TOY_AST_NODE_ERROR:
case TOY_AST_NODE_LITERAL:
case TOY_AST_NODE_BREAK:
@@ -429,8 +435,6 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
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;
@@ -550,7 +554,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
//write each piece of the declaration to the bytecode
int identifierIndex = Toy_findLiteralIndex(&compiler->literalCache, node->varDecl.identifier);
int identifierIndex = Toy_private_findLiteralIndex(&compiler->literalCache, node->varDecl.identifier);
if (identifierIndex < 0) {
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->varDecl.identifier);
}
@@ -583,9 +587,13 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
Toy_initCompiler(fnCompiler);
Toy_writeCompiler(fnCompiler, node->fnDecl.arguments); //can be empty, but not NULL
Toy_writeCompiler(fnCompiler, node->fnDecl.returns); //can be empty, but not NULL
Toy_Opcode override = Toy_writeCompilerWithJumps(fnCompiler, node->fnDecl.block, NULL, NULL, -4, rootNode); //can be empty, but not NULL
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
//BUGFIX: copied from TOY_AST_NODE_BLOCK, omitting the SCOPE_BEGIN and SCOPE_END opcodes (might squeeze a few bytes out of the interpreter's scopes by declaring one less)
for (int i = 0; i < node->fnDecl.block->block.count; i++) {
Toy_Opcode override = Toy_writeCompilerWithJumps(fnCompiler, &(node->fnDecl.block->block.nodes[i]), NULL, NULL, -4, &(node->fnDecl.block->block.nodes[i]));
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
fnCompiler->bytecode[fnCompiler->count++] = (unsigned char)override; //1 byte
}
}
//adopt the panic state if anything happened
@@ -597,7 +605,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
Toy_Literal fnLiteral = ((Toy_Literal){ .as = { .generic = fnCompiler }, .type = TOY_LITERAL_FUNCTION_INTERMEDIATE});
//push the name
int identifierIndex = Toy_findLiteralIndex(&compiler->literalCache, node->fnDecl.identifier);
int identifierIndex = Toy_private_findLiteralIndex(&compiler->literalCache, node->fnDecl.identifier);
if (identifierIndex < 0) {
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnDecl.identifier);
}
@@ -645,7 +653,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
for (int i = 0; i < node->fnCall.arguments->fnCollection.count; i++) { //reverse order, to count from the beginning in the interpreter
//sub-calls
if (node->fnCall.arguments->fnCollection.nodes[i].type != TOY_AST_NODE_LITERAL) {
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, &node->fnCall.arguments->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, &node->fnCall.arguments->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr, jumpOffsets, node); //BUGFIX: use node as rootNode, to allow indexing within argument lists
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
@@ -653,7 +661,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
//write each argument to the bytecode
int argumentsIndex = Toy_findLiteralIndex(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
int argumentsIndex = Toy_private_findLiteralIndex(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
if (argumentsIndex < 0) {
argumentsIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
}
@@ -675,7 +683,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
//push the argument COUNT to the top of the stack
Toy_Literal argumentsCountLiteral = TOY_TO_INTEGER_LITERAL(node->fnCall.argumentCount); //argumentCount is set elsewhere to support dot operator
int argumentsCountIndex = Toy_findLiteralIndex(&compiler->literalCache, argumentsCountLiteral);
int argumentsCountIndex = Toy_private_findLiteralIndex(&compiler->literalCache, argumentsCountLiteral);
if (argumentsCountIndex < 0) {
argumentsCountIndex = Toy_pushLiteralArray(&compiler->literalCache, argumentsCountLiteral);
}
@@ -917,6 +925,54 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
break;
case TOY_AST_NODE_AND: {
//process the lhs
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->pathAnd.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//insert the AND opcode to signal a possible jump
compiler->bytecode[compiler->count++] = TOY_OP_AND; //1 byte
int jumpToEnd = compiler->count;
compiler->count += sizeof(unsigned short); //2 bytes
//process the rhs
override = Toy_writeCompilerWithJumps(compiler, node->pathAnd.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//set the spot to jump to, to proceed
unsigned short tmpVal = compiler->count + jumpOffsets;
memcpy(compiler->bytecode + jumpToEnd, &tmpVal, sizeof(tmpVal));
}
break;
case TOY_AST_NODE_OR: {
//process the lhs
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->pathOr.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//insert the AND opcode to signal a possible jump
compiler->bytecode[compiler->count++] = TOY_OP_OR; //1 byte
int jumpToEnd = compiler->count;
compiler->count += sizeof(unsigned short); //2 bytes
//process the rhs
override = Toy_writeCompilerWithJumps(compiler, node->pathOr.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//set the spot to jump to, to proceed
unsigned short tmpVal = compiler->count + jumpOffsets;
memcpy(compiler->bytecode + jumpToEnd, &tmpVal, sizeof(tmpVal));
}
break;
case TOY_AST_NODE_FN_RETURN: {
//read each returned literal onto the stack, and return the number of values to return
for (int i = 0; i < node->returns.returns->fnCollection.count; i++) {
+43 -5
View File
@@ -1,11 +1,20 @@
#pragma once
/*!
# toy_compiler.h
This header defines the compiler structure, which is used to transform abstract syntax trees into usable intermediate bytecode. There are two steps to generating bytecode - the writing step, and the collation step.
During the writing step, the core of the program is generated, along with a series of literals representing the values within the program; these values are compressed and flattened into semi-unrecognizable forms. If the same literal is used multiple times in a program, such as a variable name, the name itself is replaced by a reference to the flattened literals within the cache.
During the collation step, everything from the core programs execution instructions, the flattened literals, the functions (which have their own sections and protocols within the bytecode) and version information (such as the macros defined in toy_common.h) are all combined into a single buffer of bytes, known as bytecode. This bytecode can then be safely saved to a file or immediately executed.
!*/
#include "toy_common.h"
#include "toy_opcodes.h"
#include "toy_ast_node.h"
#include "toy_literal_array.h"
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
typedef struct Toy_Compiler {
Toy_LiteralArray literalCache;
unsigned char* bytecode;
@@ -14,9 +23,38 @@ typedef struct Toy_Compiler {
bool panic;
} Toy_Compiler;
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
/*!
## Define Functions
//embed the header, data section, code section, function section, etc.
Executing the following functions out-of-order causes undefiend behaviour.
!*/
/*!
### void Toy_initCompiler(Toy_Compiler* compiler)
This function initializes the given compiler.
!*/
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
/*!
### void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node)
This function writes the given `node` argument to the compiler. During the writing step, this function may be called repeatedly, with a stream of results from `Toy_scanParser()`, until `Toy_scanParser()` returns `NULL`.
!*/
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
/*!
### unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size)
This function returns a buffer of bytes, known as "bytecode", created from the given compiler; it also stores the size of the bytecode in the variable pointed to by `size`.
Calling `Toy_collateCompiler()` multiple times on the same compiler will produce undefined behaviour.
!*/
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size);
/*!
### void Toy_freeCompiler(Toy_Compiler* compiler)
This function frees a compiler. Calling this on a compiler which has not been collated will free that compiler as expected - anything written to it will be lost.
!*/
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
-12
View File
@@ -1,12 +0,0 @@
#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);
+450 -405
View File
File diff suppressed because it is too large Load Diff
+160 -11
View File
@@ -1,5 +1,41 @@
#pragma once
/*!
# toy_interpreter.h
This header defines the interpreter structure, which is the beating heart of Toy.
`Toy_Interpreter` is a stack-based, bytecode-driven interpreter with a number of customisation options, including "hooks"; native C functions wrapped in `Toy_Literal` instances, injected into the interpreter in order to give the Toy scripts access to libraries via the `import` keyword. The hooks, when invoked this way, can then inject further native functions into the interpreter's current scope. Exactly which hooks are made available varies by host program, but `standard` is the most commonly included one.
Another useful customisation feature is the ability to redicrect output from the `print` and `assert` keywords, as well as any internal errors that occur. This can allow you to add in a logging system, or even hook the `print` statement up to some kind of HUD.
## Defined Interfaces
Note: These interfaces are *actually* defined in [toy_literal.h](toy_literal_h.md) but are documented here, because this is where it matters most.
### typedef void (*Toy_PrintFn)(const char*)
This is the interface used by "print functions" - that is, functions used to print messages from the `print` and `assert` keywords, as well as internal interpreter errors.
### typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments)
This is the interface used by "native functions" - that is, functions written in C which can be called directly by Toy scripts.
The arguments to the function are passed in as a `Toy_LiteralArray`.
### typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias)
This is the interface used by "hook functions" - that is, functions written in C which are invoked by using the `import` keyword, and are intended to inject other native functions into the current scope. While hook functions are capable of doing other things, this is greatly discouraged.
The identifier of the library (its name) is passed in as a `Toy_Literal`, as is any given alias; if no alias is given, then `alias` will be a null literal. Here, the identifier is `standard`, while the alias is `std`.
```
import standard as std;
```
Conventionally, when an alias is given, all of the functions should instead be inserted into a `Toy_LiteralDictionary` which is then inserted into the scope with the alias as its identifier.
!*/
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_literal_array.h"
@@ -31,21 +67,134 @@ typedef struct Toy_Interpreter {
bool panic;
} Toy_Interpreter;
//native API
/*!
## Defined Functions
!*/
/*!
### void Toy_initInterpreter(Toy_Interpreter* interpreter)
This function initializes the interpreter. It allocates memory for internal systems such as the stack, and zeroes-out systems that have yet to be invoked. Internally, it also invokes `Toy_resetInterpreter` to initialize the environment.
!*/
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
/*!
### void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length)
This function takes a `Toy_Interpreter` and `bytecode` (as well as the `length` of the bytecode), checks its version information, parses and un-flattens the literal cache, and executes the compiled program stored in the bytecode. This function also consumes the bytecode, so the `bytecode` argument is no longer valid after calls.
If the given bytecode's embedded version is not compatible with the current interpreter, then this function will refuse to execute.
Re-using a `Toy_Interpreter` instance without first resetting it is possible (that's how the repl works), however doing so may have unintended consequences if the scripts are not intended to be used in such a way. Any variables declared will persist.
!*/
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length);
/*!
### void Toy_resetInterpreter(Toy_Interpreter* interpreter)
This function frees any scopes that the scripts have built up, and generates a new one. It also injects several globally available functions:
* set
* get
* push
* pop
* length
* clear
!*/
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter);
/*!
### void Toy_freeInterpreter(Toy_Interpreter* interpreter)
This function frees a `Toy_Interpreter`, clearing all of the memory used within. That interpreter is no longer valid for use, and must be re-initialized.
!*/
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter);
/*!
### bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func)
This function will inject the given native function `func` into the `Toy_Interpreter`'s current scope, with the identifer as `name`. Both the name and function will be converted into literals internally before being stored. It will return true on success, otherwise it will return false.
The primary use of this function is within hooks.
!*/
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func);
/*!
### bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook)
This function will inject the given native function `hook` into the `Toy_Interpreter`'s hook cache, with the identifier as `name`. Both the name and the function will be converted into literals internally before being stored. It will return true on success, otherwise it will return false.
Hooks are invoked with the `import` keyword within Toy's scripts.
!*/
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook);
/*!
### bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns)
This function calls a `Toy_Literal` which contains a function, with the arguments to that function passed in as `arguments` and the results stored in `returns`. It returns true on success, otherwise it returns false.
The literal `func` can be either a native function or a Toy function, but it won't execute a hook.
!*/
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
/*!
### bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns)
This utility function will find a `Toy_literal` within the `Toy_Interpreter`'s scope with an identifier that matches `name`, and will invoke it using `Toy_callLiteralFn` (passing in `arguments` and `returns` as expected).
!*/
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
//utilities for the host program
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
/*!
### bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr)
//main access
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length); //run the code
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
Sometimes, native functions will receive `Toy_Literal` identifiers instead of the values - the correct values can be retreived from the given interpreter's scope using the following pattern:
```c
Toy_Literal foobarIdn = foobar;
if (TOY_IS_IDENTIFIER(foobar) && Toy_parseIdentifierToValue(interpreter, &foobar)) {
freeLiteral(foobarIdn); //remember to free the identifier
}
```
!*/
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
/*!
### void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput)
This function sets the function called by the `print` keyword. By default, the following wrapper is used:
```c
static void printWrapper(const char* output) {
printf("%s\n", output);
}
```
Note: The above is a very minor lie - in reality there are some preprocessor directives to allow the repl's `-n` flag to work.
!*/
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
/*!
### void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput)
This function sets the function called by the `assert` keyword on failure. By default, the following wrapper is used:
```c
static void assertWrapper(const char* output) {
fprintf(stderr, "Assertion failure: %s\n", output);
}
```
!*/
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
/*!
### void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput)
This function sets the function called when an error occurs within the interpreter. By default, the following wrapper is used:
```c
static void errorWrapper(const char* output) {
fprintf(stderr, "%s", output); //no newline
}
```
!*/
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
+3 -3
View File
@@ -237,7 +237,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
//scan for a keyword
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
if (strlen(Toy_keywordTypes[i].keyword) == (size_t)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
Toy_Token token;
token.type = Toy_keywordTypes[i].type;
@@ -317,10 +317,10 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
} else {
return makeToken(lexer, TOY_TOKEN_AND);
return makeToken(lexer, TOY_TOKEN_AND_AND);
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR_OR : TOY_TOKEN_PIPE);
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
+37 -1
View File
@@ -1,5 +1,11 @@
#pragma once
/*!
# toy_lexer.h
This header defines the lexer and token structures, which can be bound to a piece of source code, and used to tokenize it within a parser.
!*/
#include "toy_common.h"
#include "toy_token_types.h"
@@ -20,10 +26,40 @@ typedef struct {
int line;
} Toy_Token;
/*!
## Defined Functions
!*/
/*!
### void Toy_initLexer(Toy_Lexer* lexer, const char* source)
This function initializes a lexer, binding it to the `source` parameter; the lexer is now ready to be passed to the parser.
!*/
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
/*!
### Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer)
This function "scans" the lexer, returning a token to the parser.
Private functions are not intended for general use.
!*/
TOY_API Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
//for debugging
/*!
### void Toy_private_printToken(Toy_Token* token)
This function prints a given token to stdout.
Private functions are not intended for general use.
!*/
TOY_API void Toy_private_printToken(Toy_Token* token);
/*!
### void Toy_private_setComments(Toy_Lexer* lexer, bool enabled)
This function sets whether comments are allowed within source code. By default, comments are allowed, and are only disabled in the repl.
Private functions are not intended for general use.
!*/
TOY_API void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
+229 -9
View File
@@ -1,5 +1,15 @@
#pragma once
/*!
# toy_literal.h
This header defines the literal structure, which is used extensively throughout Toy to represent values of some kind.
The main way of interacting with literals is to use a macro of some kind, as the exact implementation of `Toy_Literal` has and will change based on the needs of Toy.
User data can be passed around within Toy as an opaque type - use the tag value for determining what kind of opaque it is, or leave it as 0.
!*/
#include "toy_common.h"
#include "toy_refstring.h"
@@ -15,6 +25,31 @@ typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_Lite
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
typedef void (*Toy_PrintFn)(const char*);
/*!
## Defined Enums
### Toy_LiteralType
* `TOY_LITERAL_NULL`
* `TOY_LITERAL_BOOLEAN`
* `TOY_LITERAL_INTEGER`
* `TOY_LITERAL_FLOAT`
* `TOY_LITERAL_STRING`
* `TOY_LITERAL_ARRAY`
* `TOY_LITERAL_DICTIONARY`
* `TOY_LITERAL_FUNCTION`
* `TOY_LITERAL_FUNCTION_NATIVE`
* `TOY_LITERAL_FUNCTION_HOOK`
* `TOY_LITERAL_IDENTIFIER`
* `TOY_LITERAL_TYPE`
* `TOY_LITERAL_OPAQUE`
* `TOY_LITERAL_ANY`
These are the main values of `Toy_LiteralType`, each of which represents a potential state of the `Toy_Literal` structure. Do not interact with a literal without determining its type with the `IS_*` macros first.
Other type values are possible, but are only used internally.
!*/
typedef enum {
TOY_LITERAL_NULL,
TOY_LITERAL_BOOLEAN,
@@ -89,6 +124,28 @@ typedef struct Toy_Literal {
//shenanigans with byte alignment reduces the size of Toy_Literal
} Toy_Literal;
/*!
## Defined Macros
!*/
/*!
The following macros are used to determine if a given literal, passed in as `value`, is of a specific type. It should be noted that `TOY_IS_FUNCTION` will return false for native and hook functions.
* `TOY_IS_NULL(value)`
* `TOY_IS_BOOLEAN(value)`
* `TOY_IS_INTEGER(value)`
* `TOY_IS_FLOAT(value)`
* `TOY_IS_STRING(value)`
* `TOY_IS_ARRAY(value)`
* `TOY_IS_DICTIONARY(value)`
* `TOY_IS_FUNCTION(value)`
* `TOY_IS_FUNCTION_NATIVE(value)`
* `TOY_IS_FUNCTION_HOOK(value)`
* `TOY_IS_IDENTIFIER(value)`
* `TOY_IS_TYPE(value)`
* `TOY_IS_OPAQUE(value)`
!*/
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
@@ -103,6 +160,23 @@ typedef struct Toy_Literal {
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
/*!
The following macros are used to cast a literal to a specific C type to be used.
* `TOY_AS_BOOLEAN(value)`
* `TOY_AS_INTEGER(value)`
* `TOY_AS_FLOAT(value)`
* `TOY_AS_STRING(value)`
* `TOY_AS_ARRAY(value)`
* `TOY_AS_DICTIONARY(value)`
* `TOY_AS_FUNCTION(value)`
* `TOY_AS_FUNCTION_NATIVE(value)`
* `TOY_AS_FUNCTION_HOOK(value)`
* `TOY_AS_IDENTIFIER(value)`
* `TOY_AS_TYPE(value)`
* `TOY_AS_OPAQUE(value)`
!*/
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
#define TOY_AS_INTEGER(value) ((value).as.integer)
#define TOY_AS_FLOAT(value) ((value).as.number)
@@ -116,6 +190,24 @@ typedef struct Toy_Literal {
#define TOY_AS_TYPE(value) ((value).as.type)
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
/*!
The following macros are used to create a new literal, with the given `value` as it's internal value.
* `TOY_TO_NULL_LITERAL` - does not need parantheses
* `TOY_TO_BOOLEAN_LITERAL(value)`
* `TOY_TO_INTEGER_LITERAL(value)`
* `TOY_TO_FLOAT_LITERAL(value)`
* `TOY_TO_STRING_LITERAL(value)`
* `TOY_TO_ARRAY_LITERAL(value)`
* `TOY_TO_DICTIONARY_LITERAL(value)`
* `TOY_TO_FUNCTION_LITERAL(value, l)` - `l` represents the length of the bytecode passed as `value`
* `TOY_TO_FUNCTION_NATIVE_LITERAL(value)`
* `TOY_TO_FUNCTION_HOOK_LITERAL(value)`
* `TOY_TO_IDENTIFIER_LITERAL(value)`
* `TOY_TO_TYPE_LITERAL(value, c)` - `c` is the true of the type should be const
* `TOY_TO_OPAQUE_LITERAL(value, t)` - `t` is the integer tag
!*/
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL})
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN})
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER})
@@ -130,31 +222,159 @@ typedef struct Toy_Literal {
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE})
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE})
//BUGFIX: For blank indexing
//BUGFIX: For blank indexing - not for general use
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
TOY_API void Toy_freeLiteral(Toy_Literal literal);
/*!
## More Defined Macros
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
The following macros are utilities used throughout Toy's internals, and are available for the user as well.
!*/
/*!
### TOY_IS_TRUTHY(x)
Returns true of the literal `x` is truthy, otherwise it returns false.
Currently, every value is considered truthy except `false`, which is falsy and `null`, which is neither true or false.
!*/
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
/*!
### TOY_AS_FUNCTION_BYTECODE_LENGTH(lit)
Returns the length of a Toy function's bytecode.
This macro is only valid on `TOY_LITERAL_FUNCTION`.
!*/
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) (Toy_lengthRefFunction((lit).inner.ptr))
/*!
### TOY_MAX_STRING_LENGTH
The maximum length of a string in Toy, which is 4096 bytes by default. This can be changed at compile time, but the results of doing so are not officially supported.
!*/
#define TOY_MAX_STRING_LENGTH 4096
/*!
### TOY_HASH_I(lit)
Identifiers are the names of values within Toy; to speed up execution, their "hash value" is computed at compile time and stored within them. Use this to access it, if needed.
This macro is only valid on `TOY_LITERAL_IDENTIFIER`.
!*/
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
/*!
### TOY_TYPE_PUSH_SUBTYPE(lit, subtype)
When building a complex type, such as the type of an array or dictionary, you may need to specify inner types. Use this to push a `subtype`. calling `Toy_freeLiteral()` on the outermost type should clean up all inner types, as expected.
This macro returns the index of the newly pushed value within it's parent.
This macro is only valid on `TOY_LITERAL_TYPE`, for both `type` and `subtype`.
!*/
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
/*!
### TOY_GET_OPAQUE_TAG(o)
Returns the value of the opaque `o`'s tag.
This macro is only valid on `TOY_LITERAL_OPAQUE`.
!*/
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
//BUGFIX: macros are not functions
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
/*!
## Defined Functions
!*/
//utils
/*!
### void Toy_freeLiteral(Toy_Literal literal)
This function frees the given literal's memory. Any internal pointers are now invalid.
This function should be called on EVERY literal when it is no longer needed, regardless of type.
!*/
TOY_API void Toy_freeLiteral(Toy_Literal literal);
/*!
### Toy_Literal Toy_copyLiteral(Toy_Literal original)
This function returns a copy of the given literal. Literals should never be copied without this function, as it handles a lot of internal memory allocations.
!*/
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
/*!
### bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs)
This checks to see if two given literals are equal.
When an integer and a float are compared, the integer is cooerced into a float for the duration of the call.
Arrays or dictionaries are equal only if their keys and values all equal. Likewise, types only equal if all subtypes are equal, in order.
Functions and opaques are never equal to anything, while values with the type `TOY_LITERAL_ANY` are always equal.
!*/
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
/*!
### int Toy_hashLiteral(Toy_Literal lit)
This finds the hash of a literal, for various purposes. Different hashing algorithms are used for different types, and some types can't be hashed at all.
types that can't be hashed are
* all kinds of functions
* type
* opaque
* any
In the case of identifiers, their hashes are precomputed on creation and are stored within the literal.
!*/
TOY_API int Toy_hashLiteral(Toy_Literal lit);
//not thread-safe
/*!
### void Toy_printLiteral(Toy_Literal literal)
This wraps a call to `Toy_printLiteralCustom`, with a printf-stdout wrapper as `printFn`.
!*/
TOY_API void Toy_printLiteral(Toy_Literal literal);
/*!
### void Toy_printLiteralCustom(Toy_Literal literal, PrintFn printFn)
This function passes the string representation of `literal` to `printFn`.
This function is not thread safe - due to the loopy and recursive nature of printing compound values, this function uses some globally persistent variables.
!*/
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn);
/*!
### bool Toy_private_isTruthy(Toy_Literal x)
Utilized by the `TOY_IS_TRUTHY` macro.
Private functions are not intended for general use.
!*/
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
/*!
### bool Toy_private_toIdentifierLiteral(Toy_RefString* ptr)
Utilized by the `TOY_TO_IDENTIFIER_LITERAL` macro.
Private functions are not intended for general use.
!*/
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
/*!
### bool Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype)
Utilized by the `TOY_TYPE_PUSH_SUBTYPE` macro.
Private functions are not intended for general use.
!*/
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
+1 -1
View File
@@ -52,7 +52,7 @@ Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
}
//find a literal in the array that matches the "literal" argument
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
int Toy_private_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
for (int i = 0; i < array->count; i++) {
//not the same type
if (array->literals[i].type != literal.type) {
+62 -2
View File
@@ -1,5 +1,13 @@
#pragma once
/*!
# literal_array.h
This header defines the array structure, which manages a series of `Toy_Literal` instances in sequential memory. The array does not take ownership of given literals, instead it makes an internal copy.
The array type is one of two fundemental data structures used throughout Toy - the other is the dictionary.
!*/
#include "toy_common.h"
#include "toy_literal.h"
@@ -10,13 +18,65 @@ typedef struct Toy_LiteralArray {
int count;
} Toy_LiteralArray;
/*!
## Defined Functions
!*/
/*
### void Toy_initLiteralArray(Toy_LiteralArray* array)
This function initializes a `Toy_LiteralArray` pointed to by `array`.
*/
TOY_API void Toy_initLiteralArray(Toy_LiteralArray* array);
/*!
### void Toy_freeLiteralArray(Toy_LiteralArray* array)
This function frees a `Toy_LiteralArray` pointed to by `array`. Every literal within is passed to `Toy_freeLiteral()` before its memory is released.
!*/
TOY_API void Toy_freeLiteralArray(Toy_LiteralArray* array);
/*!
### int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal)
This function adds a new `literal` to the end of the `array`, growing the array's internal buffer if needed.
This function returns the index of the inserted value.
!*/
TOY_API int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal);
/*!
### Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array)
This function removes the literal at the end of the `array`, and returns it.
!*/
TOY_API Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array);
/*!
### bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value)
This function frees the literal at the position represented by the integer literal `index`, and stores `value` in its place.
This function returns true on success, otherwise it returns false.
!*/
TOY_API bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value);
/*!
### Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index)
This function returns the literal at the position represented by the integer literal `index`, or returns a null literal if none is found.
If `index` is not an integer literal or is out of bounds, this function returns a null literal.
!*/
TOY_API Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index);
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
/*!
### int Toy_private_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal)
//TODO: add a function to get the capacity & count
This function scans through the array, and returns the index of the first element that matches the given `literal`, otherwise it returns -1.
Private functions are not intended for general use.
!*/
int Toy_private_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
//TODO: add a function to get the capacity & count
-1
View File
@@ -131,7 +131,6 @@ static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
//exposed functions
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 = 0;
dictionary->contains = 0;
+67
View File
@@ -1,9 +1,31 @@
#pragma once
/*!
# toy_literal_dictionary.h
This header defines the dictionary structure (as well as the private entry structure), which manages a series of `Toy_Literal` instances stored in a key-value hash map. The dictionary does not take ownership of given literals, instead it makes an internal copy.
The dictionary type is one of two fundemental data structures used throughout Toy - the other is the array.
!*/
#include "toy_common.h"
#include "toy_literal.h"
/*!
## Defined Macros
!*/
/*!
### TOY_DICTIONARY_MAX_LOAD
If the contents of a dictionary exceeds this percentage of it's capacity, then a new buffer is created, the old contents are copied over one-by-one, and the original buffer is freed.
Since this process can be memory and time intensive, a configurable macro is used to allow for fine-grained control across the lang.
The current default value is `0.75`, representing 75% capacity.
!*/
//TODO: benchmark this
#define TOY_DICTIONARY_MAX_LOAD 0.75
@@ -19,11 +41,56 @@ typedef struct Toy_LiteralDictionary {
int contains; //count + tombstones, for internal use
} Toy_LiteralDictionary;
/*!
## Defined Functions
!*/
/*!
### void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary)
This function initializes the `Toy_LiteralDictionary` pointed to by `dictionary`.
!*/
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
/*!
### void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary)
This function frees a `Toy_LiteralDictionary` pointed to by `dictionary`. Every literal within is passed to `Toy_freeLiteral()` before its memory is released.
!*/
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
/*!
### void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value)
This function inserts the given key-value pair of literals into `dictionary`, creating it if it doesn't exist, or freeing and overwriting it if `key` is already present. This function may also expand the memory buffer if needed.
When expanding the memory buffer, a full copy of the existing dictionary's contents is created - this can be memory intensive.
Literal functions and opaques cannot be used as keys.
!*/
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
/*!
### Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
This function returns the value of the literal within `dictionary` identified by `key`, or a null literal if it doesn't exist.
Literal functions and opaques cannot be used as keys.
!*/
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
/*!
### void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
This function removes the key-value pair of literals from `dictionary` identified by `key`, if it exists.
Literal functions and opaques cannot be used as keys.
!*/
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
/*!
### bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
This function returns true if the key-value pair identified by `key` exists within `dictionary`, otherwise it returns false.
!*/
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
+94 -3
View File
@@ -1,21 +1,112 @@
#pragma once
/*!
# toy_memory.h
This header defines all of the memory management utilities. Any and all heap-based memory management goes through these utilities.
A default memory allocator function is used internally, but it can be overwritten for diagnostic and platform related purposes.
!*/
#include "toy_common.h"
/*!
## Defined Macros
!*/
/*!
### TOY_GROW_CAPACITY(capacity)
This macro calculates, in place, what size of memory should be allocated based on the previous size.
!*/
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
/*!
### TOY_GROW_CAPACITY_FAST(capacity)
This macro calculates, in place, what size of memory should be allocated based on the previous size. It grows faster than `TOY_GROW_CAPACITY`.
!*/
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
/*
### TOY_ALLOCATE(type, count)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be allocated, and `count` is the number which will be needed (usually calculated with `TOY_GROW_CAPACITY`).
This returns a pointer of `type`.
*/
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
/*!
### TOY_FREE(type, pointer)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be freed, and `pointer` is to what is being freed. This should only be used when a single element has been allocated, as opposed to an array.
!*/
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
/*!
### TOY_FREE_ARRAY(type, pointer, oldCount)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be freed, `pointer` is a reference to what is being freed, and `oldCount` is the size of the array being freed. This should only be used when an array has been allocated, as opposed to a single element.
!*/
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
/*!
### TOY_GROW_ARRAY(type, pointer, oldCount, count)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that is being operated on, `pointer` is what is being resized, `oldCount` is the previous size of the array and `count` is the new size of the array (usually calculated with `TOY_GROW_CAPACITY`).
This returns a pointer of `type`.
!*/
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
/*!
### TOY_SHRINK_ARRAY(type, pointer, oldCount, count)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that is being operated on, `pointer` is what is being resized, `oldCount` is the previous size of the array and `count` is the new size of the array.
This returns a pointer of `type`.
!*/
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
//implementation details
/*!
## Defined Interfaces
!*/
/*!
### typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
This function interface is used for defining any memory allocator functions.
Any and all memory allocator functions should:
* Take a `pointer` to a previously allocated block of memory, or `NULL`
* Take the `oldSize`, which is the previous size of the `pointer` allocated, in bytes (`oldSize` can be 0)
* Take the `newSize`, which is the new size of the buffer to be allocaated, in bytes (`newSize` can be 0)
* Return the newly allocated buffer, or `NULL` if `newSize` is zero
* Return `NULL` on error
!*/
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
## Defined Functions
!*/
/*!
### TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize)
This function shouldn't be called directly. Instead, use one of the given macros.
This function wraps a call to the internal assigned memory allocator.
!*/
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
//assign the memory allocator
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
### void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn)
This function sets the memory allocator, replacing the default memory allocator.
This function also overwrites any given refstring and reffunction memory allocators, see [toy_refstring.h](toy_refstring_h.md).
!*/
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
+2 -2
View File
@@ -29,8 +29,8 @@ typedef enum Toy_Opcode {
TOY_OP_SCOPE_BEGIN,
TOY_OP_SCOPE_END,
TOY_OP_TYPE_DECL, //declare a type to be used (as a literal)
TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
TOY_OP_TYPE_DECL_removed,
TOY_OP_TYPE_DECL_LONG_removed,
TOY_OP_VAR_DECL, //declare a variable to be used (as a literal)
TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
+40 -27
View File
@@ -168,6 +168,10 @@ static Toy_Opcode compound(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
consume(parser, TOY_TOKEN_COMMA, "Expected ',' in array or dictionary");
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) { //allow for trailing commas
break;
}
iterations++;
Toy_ASTNode* left = NULL;
@@ -335,6 +339,28 @@ static Toy_Opcode grouping(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
}
}
static Toy_Opcode circuit(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
//handle short-circuitable operators - && ||
switch (parser->previous.type) {
case TOY_TOKEN_AND_AND: {
parsePrecedence(parser, nodeHandle, PREC_AND + 1);
return TOY_OP_AND;
}
case TOY_TOKEN_OR_OR: {
parsePrecedence(parser, nodeHandle, PREC_OR + 1);
return TOY_OP_OR;
}
default: {
error(parser, parser->previous, "Unexpected token passed to grouping precedence rule");
return TOY_OP_EOF;
}
}
}
static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
@@ -428,16 +454,6 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
return TOY_OP_COMPARE_GREATER_EQUAL;
}
case TOY_TOKEN_AND: {
parsePrecedence(parser, nodeHandle, PREC_AND + 1);
return TOY_OP_AND;
}
case TOY_TOKEN_OR: {
parsePrecedence(parser, nodeHandle, PREC_OR + 1);
return TOY_OP_OR;
}
default:
error(parser, parser->previous, "Unexpected token passed to binary precedence rule");
return TOY_OP_EOF;
@@ -750,23 +766,10 @@ static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
static Toy_Opcode fnCall(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//wait - is the previous token a type? this should be casting instead
if (parser->previous.type >= TOY_TOKEN_NULL && parser->previous.type <= TOY_TOKEN_ANY) {
//casting type
Toy_ASTNode* lhsNode = NULL;
castingPrefix(parser, &lhsNode);
advance(parser);
//casting value
Toy_ASTNode* rhsNode = NULL;
grouping(parser, &rhsNode);
parsePrecedence(parser, nodeHandle, PREC_CALL);
//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
return TOY_OP_TYPE_CAST; //opcode value
}
advance(parser); //skip the left paren
@@ -1011,8 +1014,8 @@ ParseRule parseRules[] = { //must match the token types
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER,
{NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL,
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL,
{NULL, binary, PREC_AND},// TOKEN_AND,
{NULL, binary, PREC_OR},// TOKEN_OR,
{NULL, circuit, PREC_AND},// TOKEN_AND,
{NULL, circuit, PREC_OR},// TOKEN_OR,
//other operators
{NULL, question, PREC_TERNARY}, //TOKEN_QUESTION,
@@ -1294,6 +1297,16 @@ static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, Preced
continue;
}
if (opcode == TOY_OP_AND) {
Toy_emitASTNodeAnd(nodeHandle, rhsNode);
continue;
}
if (opcode == TOY_OP_OR) {
Toy_emitASTNodeOr(nodeHandle, rhsNode);
continue;
}
Toy_emitASTNodeBinary(nodeHandle, rhsNode, opcode);
//optimise away the constants
+84 -1
View File
@@ -1,10 +1,62 @@
#pragma once
/*!
# toy_parser.h
This header defines the parser structure which, after being initialized with a lexer produces a series of abstract syntax trees to be passed to the compiler. The following is a utility function provided by [repl_tools.h](repl_tools_h.md), demonstrating how to use the parser.
```c
//generate bytecode from a given string
const unsigned char* Toy_compileString(const char* source, size_t* size) {
//declare the relevant instances
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
//initialize each of them
Toy_initLexer(&lexer, source);
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//when the parser returns NULL, it is finished
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//if the parser returns an error node, clean up and exit gracefully
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
//no need to clean the lexer
return NULL;
}
//write the node to the compiler
Toy_writeCompiler(&compiler, node);
Toy_freeASTNode(node);
//grab the next node
node = Toy_scanParser(&parser);
}
//get the bytecode to be returned
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
//cleanup
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
//no need to clean the lexer
//finally
return tb;
}
```
!*/
#include "toy_common.h"
#include "toy_lexer.h"
#include "toy_ast_node.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
//Parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
Toy_Lexer* lexer;
bool error; //I've had an error
@@ -15,6 +67,37 @@ typedef struct {
Toy_Token previous;
} Toy_Parser;
/*!
## Defined Functions
!*/
/*!
### void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer)
This function initializes a `Toy_Parser`, binding the given `Toy_Lexer` to it.
!*/
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
/*!
### void Toy_freeParser(Toy_Parser* parser)
This function frees a `Toy_Parser` once its task is completed.
!*/
TOY_API void Toy_freeParser(Toy_Parser* parser);
/*!
### Toy_ASTNode* Toy_scanParser(Toy_Parser* parser)
This function returns an abstract syntax tree representing part of the program, or an error node. The abstract syntax tree must be passed to `Toy_writeCompiler()` and/or `Toy_freeASTNode()`.
This function should be called repeatedly until it returns `NULL`, indicating the end of the program.
!*/
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
/*!
### void Toy_freeASTNode(Toy_ASTNode* node)
This function cleans up any valid instance of `Toy_ASTNode` pointer passed to it. It is most commonly used to clean up the values returned by `Toy_scanParser`, after they have been passsed to `Toy_writeCompiler`, or when the node is an error node.
Note: this function is *actually* defined in toy_ast_node.h, but documented here, because this is where it matters most.
!*/
+76 -11
View File
@@ -1,10 +1,16 @@
#pragma once
#include "toy_common.h"
/*!
# toy_reffunction.h
//memory allocation hook
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
This header defines the Toy_RefFunction structure, as well as all of the related utilities.
See [Toy_RefString](toy_refstring_h.md) for more information about the reference pattern.
This module reserves the right to instead preform a deep copy when it sees fit (this is for future debugging purposes).
!*/
#include "toy_common.h"
//the RefFunction structure
typedef struct Toy_RefFunction {
@@ -13,11 +19,70 @@ typedef struct Toy_RefFunction {
unsigned char data[];
} Toy_RefFunction;
//API
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
/*!
## Defined Interfaces
!*/
/*!
### typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
This interface conforms to Toy's memory API, and generally shouldn't be used without a good reason.
!*/
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
## Defined Functions
!*/
/*!
### void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn)
This function conforms to and is invoked by Toy's memory API, and generally shouldn't be used without a good reason.
!*/
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
/*!
### Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length)
This function returns a new `Toy_RefFunction`, containing a copy of `data`, or `NULL` on error.
This function also sets the returned `refFunction`'s reference counter to 1.
!*/
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
/*!
### void Toy_deleteRefFunction(Toy_RefFunction* refFunction)
This function reduces the `refFunction`'s reference counter by 1 and, if it reaches 0, frees the memory.
!*/
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
/*!
### int Toy_countRefFunction(Toy_RefFunction* refFunction)
This function returns the total number of references to `refFunction`, for debugging.
!*/
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
/*!
### size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction)
This function returns the length of the underlying bytecode of `refFunction`.
!*/
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
/*!
### Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction)
This function increases the reference counter of `refFunction` by 1, before returning the given pointer.
This function reserves the right to create a deep copy where needed.
!*/
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
/*!
### Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction)
This function behaves identically to `Toy_copyRefFunction`, except that it explicitly forces a deep copy of the internal memory. Using this function should be done carefully, as it incurs a performance penalty that negates the benefit of this module.
!*/
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
+100 -6
View File
@@ -1,13 +1,24 @@
#pragma once
/*!
# toy_refstring.h
This header defines the structure `Toy_RefString`, as well as all of the related utilities.
[refstring](https://github.com/Ratstail91/refstring) is a stand-alone utility written to reduce the amount of memory manipulation used within Toy. It was independantly written and tested, before being incorporated into Toy proper. As such it has it's own memory management API, which by default is tied into Toy's [core memory API](toy_memory_h.md).
Instances of `Toy_RefString` are reference counted - that is, rather than copying an existing string in memory, a pointer to the refstring is returned, and the internal reference counter is increased by 1. When the pointer is no longer needed, `Toy_DeleteRefString` can be called; this will decrement the internal reference counter by 1, and only free it when it reaches 0. This has multiple benefits, when used correctly:
* Reduced memory usage
* Faster program execution
This module reserves the right to instead preform a deep copy when it sees fit (this is for future debugging purposes).
!*/
#include "toy_common.h"
#include <string.h>
//memory allocation hook
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
TOY_API void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
//the RefString structure
typedef struct Toy_RefString {
size_t length;
@@ -15,17 +26,100 @@ typedef struct Toy_RefString {
char data[];
} Toy_RefString;
//API
/*!
## Defined Interfaces
!*/
/*!
### typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
This interface conforms to Toy's memory API, and generally shouldn't be used without a good reason.
!*/
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
## Defined Functions
!*/
/*!
### void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn)
This function conforms to and is invoked by Toy's memory API, and generally shouldn't be used without a good reason.
!*/
TOY_API void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
/*!
### Toy_RefString* Toy_createRefString(const char* cstring)
This function wraps `Toy_CreateRefStringLength`, by determining the length of the given `cstring` and passing it to the other function.
!*/
TOY_API Toy_RefString* Toy_createRefString(const char* cstring);
/*!
### Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length)
This function returns a new `Toy_RefString`, containing a copy of `cstring`, or `NULL` on error.
This function also sets the returned refstring's reference counter to 1.
!*/
TOY_API Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
/*!
### void Toy_deleteRefString(Toy_RefString* refString)
This function reduces the `refString`'s reference counter by 1 and, if it reaches 0, frees the memory.
!*/
TOY_API void Toy_deleteRefString(Toy_RefString* refString);
/*!
### int Toy_countRefString(Toy_RefString* refString)
This function returns the total number of references to `refString`, for debugging.
!*/
TOY_API int Toy_countRefString(Toy_RefString* refString);
/*!
### size_t Toy_lengthRefString(Toy_RefString* refString)
This function returns the length of the underlying cstring of `refString`.
!*/
TOY_API size_t Toy_lengthRefString(Toy_RefString* refString);
/*!
### Toy_RefString* Toy_copyRefString(Toy_RefString* refString)
This function increases the reference counter of `refString` by 1, before returning the given pointer.
This function reserves the right to create a deep copy where needed.
!*/
TOY_API Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
/*!
### Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString)
This function behaves identically to `Toy_copyRefString`, except that it explicitly forces a deep copy of the internal memory. Using this function should be done carefully, as it incurs a performance penalty that negates the benefit of this module.
!*/
TOY_API Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
/*!
### const char* Toy_toCString(Toy_RefString* refString)
This function exposes the interal cstring of `refString`. Only use this function when dealing with external APIs.
!*/
TOY_API const char* Toy_toCString(Toy_RefString* refString);
/*!
### bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs)
This function returns true when the two refstrings are either the same refstring, or contain the same value. Otherwise it returns false.
!*/
TOY_API bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
/*!
### bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring)
This function returns true when the `refString` contains the same value as the `cstring`. Otherwise it returns false.
!*/
TOY_API bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
//TODO: merge refstring memory
+1 -1
View File
@@ -256,7 +256,7 @@ bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal typ
return true;
}
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
return true;
+68 -4
View File
@@ -1,5 +1,15 @@
#pragma once
/*!
# toy_scope.h
This header defines the scope structure, which stores all of the variables used within a given block of code.
Scopes are arranged into a linked list of ancestors, each of which is reference counted. When a scope is popped off the end of the chain, every ancestor scope has it's reference counter reduced by 1 and, if any reach 0, they are freed.
This is also where Toy's type system lives.
!*/
#include "toy_literal.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
@@ -11,16 +21,70 @@ typedef struct Toy_Scope {
int references; //how many scopes point here
} Toy_Scope;
/*!
## Defined Functions
!*/
/*!
### Toy_Scope* Toy_pushScope(Toy_Scope* scope)
This function creates a new `Toy_scope` with `scope` as it's ancestor, and returns it.
!*/
TOY_API Toy_Scope* Toy_pushScope(Toy_Scope* scope);
/*!
### Toy_Scope* Toy_popScope(Toy_Scope* scope)
This function frees the given `scope`, and returns it's ancestor.
!*/
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
/*!
### Toy_Scope* Toy_copyScope(Toy_Scope* original)
This function copies an existing scope, and returns the copy.
This copies the internal dictionaries, so it can be memory intensive.
!*/
TOY_API Toy_Scope* Toy_copyScope(Toy_Scope* original);
//returns false if error
TOY_API bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
TOY_API bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
/*!
### bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type)
//return false if undefined
This function declares a new variable `key` within `scope`, giving it the type of `type`.
This function returns true on success, otherwise it returns failure (such as if the given key already exists).
!*/
TOY_API bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
/*!
### bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key)
This function checks to see if a given variable with the name `key` has been previously declared.
!*/
TOY_API bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
/*!
### bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck)
This function sets an existing variable named `key` to the value of `value`. This function fails if `constCheck` is true and the given key's type has the constaant flag set. It also fails if the given key doesn't exist.
This function returns true on success, otherwise it returns false.
!*/
TOY_API bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
/*!
### bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value)
This function sets the literal pointed to by `value` to equal the variable named `key`.
This function returns true on success, otherwise it returns false.
!*/
TOY_API bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
/*!
### Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key)
This function returns a new `Toy_Literal` representing the type of the variable named `key`.
!*/
TOY_API Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
+2 -2
View File
@@ -74,8 +74,8 @@ typedef enum Toy_TokenType {
TOY_TOKEN_GREATER,
TOY_TOKEN_LESS_EQUAL,
TOY_TOKEN_GREATER_EQUAL,
TOY_TOKEN_AND,
TOY_TOKEN_OR,
TOY_TOKEN_AND_AND,
TOY_TOKEN_OR_OR,
//other operators
TOY_TOKEN_QUESTION,
+2 -2
View File
@@ -2,9 +2,9 @@ CC=gcc
IDIR +=. ../source ../repl
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS +=
LIBS +=-lm
ODIR = obj
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c) ../repl/repl_tools.c
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c) ../repl/repl_tools.c ../repl/drive_system.c
TESTS = $(wildcard test_*.c)
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
+16
View File
@@ -10,4 +10,20 @@
}
//test function coercion
{
fn f(arg: float) {
assert typeof arg == float, "argument coercion failed";
}
f(42);
fn g(): float {
return 42;
}
assert typeof g() == float, "return coercion failed";
}
print "All good";
+11
View File
@@ -0,0 +1,11 @@
//test for casting + grouping, see #67
{
assert string(10 % 4) == "2", "basic group casting failed";
assert string 4 == "4", "normal casting failed";
assert typeof string(10 % 4) == string, "group casting type failed";
}
print "All good";
@@ -0,0 +1,20 @@
fn max(lhs, rhs) {
if (lhs > rhs) {
return lhs;
}
else {
return rhs;
}
}
var array = [42];
var result = null;
//problematic line
result = max(0, array[0]);
assert result == 42, "Indexing in argument list failed";
print "All good";
+185
View File
@@ -0,0 +1,185 @@
import math;
// test pow
{
assert pow(5, 3) == 125, "pow(5, 3) failed";
assert pow(-5, 3) == -125, "pow(-5, 3) failed";
assert pow(-5.5, 3) == -166.375, "pow(-5.5, 3) failed";
assert pow(0, 1) == 0.0, "pow(0, 1) failed";
assert pow(-0.0, 1) == -0.0, "pow(0, 1) failed";
}
// test sqrt
{
assert sqrt(25) == 5, "sqrt(25) failed";
assert sqrt(256.0) == 16, "sqrt(256.0) failed";
assert checkIsNaN(sqrt(-256.0)), "sqrt(-256.0) failed";
assert sqrt(1) == 1, "sqrt(1) failed";
assert sqrt(0) == 0, "sqrt(0) failed";
}
// test cbrt
{
assert cbrt(64) == 4, "cbrt(64) failed";
assert cbrt(4096.0) == 16, "cbrt(4096.0) failed";
assert cbrt(-64) == -4, "cbrt(-64) failed";
assert cbrt(1) == 1, "cbrt(1) failed";
assert cbrt(0) == 0, "cbrt(0) failed";
}
// test hypot
{
assert hypot(3, 4) == 5, "hypot(3, 4) failed";
}
// test toRad
{
assert toRadians(0) == 0, "toRadians(0) failed";
assert toRadians(180) == PI, "toRadians(180) failed";
assert toRadians(360) == 2 * PI, "toRadians(360) failed";
}
// test toDeg
{
assert toDegrees(0) == 0, "toDegrees(0) failed";
assert toDegrees(PI) == 180, "toDegrees(PI) failed";
assert toDegrees(2 * PI) == 360, "toDegrees(2*PI) failed";
}
// test sin
{
assert epsilionCompare(sin(PI), 0), "sin(PI) failed";
assert epsilionCompare(sin(PI / 2), 1), "sin(PI/2) failed";
assert epsilionCompare(sin(0), 0), "sin(0) failed";
}
// test cos
{
assert epsilionCompare(cos(PI), -1), "cos(PI) failed";
assert epsilionCompare(cos(PI / 2), 0), "cos(PI/2) failed";
assert epsilionCompare(cos(0), 1), "cos(0) failed";
}
// test tan
{
assert epsilionCompare(tan(PI), 0), "tan(PI) failed";
assert epsilionCompare(tan(PI / 4), 1), "tan(PI/4) failed";
assert epsilionCompare(tan(0), 0), "tan(0) failed";
}
// test asin
{
assert epsilionCompare(asin(1), 1.570796), "asin(1) failed";
assert epsilionCompare(asin(-0.5), -0.523599), "asin(-0.5) failed";
assert epsilionCompare(asin(0), 0), "asin(0) failed";
}
// test acos
{
assert epsilionCompare(acos(1), 0), "acos(1) failed";
assert epsilionCompare(acos(0.5), 1.047198), "acos(0.5) failed";
assert epsilionCompare(acos(0), 1.570796), "acos(0) failed";
}
// test atan
{
assert epsilionCompare(atan(1), 0.785398), "acos(1) failed";
assert epsilionCompare(atan(INFINITY), 1.570796), "atan(INFINITY) failed";
assert epsilionCompare(atan(0), 0), "atan(0) failed";
}
// test atan2
{
assert epsilionCompare(atans(0, 0), 0), "atan2(0, 0) failed";
assert epsilionCompare(atans(7, 0), 1.570796), "atans(7, 0) failed";
}
// test sinh
{
assert epsilionCompare(sinh(1), 1.175201), "sinh(1) failed";
assert epsilionCompare(sinh(-1), -1.175201), "sinh(-1) failed";
assert epsilionCompare(sinh(0), 0), "sinh(0) failed";
}
// test cosh
{
assert epsilionCompare(cosh(1), 1.543081), "cosh(1) failed";
assert epsilionCompare(cosh(-1), 1.543081), "cosh(-1) failed";
assert epsilionCompare(cosh(0), 1), "cosh(0) failed";
}
// test tanh
{
assert epsilionCompare(tanh(1), 0.761594), "tanh(1) failed";
assert epsilionCompare(tanh(-1), -0.761594), "tanh(-1) failed";
assert epsilionCompare(tanh(0), 0), "tanh(0) failed";
}
// test asinh
{
assert epsilionCompare(asinh(1), 0.881374), "asinh(1) failed";
assert epsilionCompare(asinh(-1), -0.881374), "asinh(-1) failed";
assert epsilionCompare(asinh(0), 0), "asinh(0) failed";
}
// test acosh
{
assert epsilionCompare(acosh(1), 0), "acosh(1) failed";
assert checkIsNaN(acosh(-1)) == true, "acosh(-1) failed";
assert checkIsNaN(acosh(0)) == true, "acosh(0) failed";
}
// test atanh
{
assert checkIsInfinite(atanh(1)) == true, "atanh(1) failed";
assert checkIsInfinite(atanh(-1)) == true, "atanh(-1) failed";
assert epsilionCompare(atanh(0), 0), "atanh(0) failed";
}
// test checkIsNaN
{
assert checkIsNaN(NAN) == true, "checkIsNaN(NAN) failed";
assert checkIsNaN(INFINITY) == false, "checkIsNaN(INFINITY) failed";
assert checkIsNaN(0.0) == false, "checkIsNaN(0.0) failed";
assert checkIsNaN(INFINITY - INFINITY) == true, "checkIsNaN(INFINITY - INFINITY) failed";
}
// test checkIsFinite
{
assert checkIsFinite(NAN) == false, "checkIsFinite(NAN) failed";
assert checkIsFinite(INFINITY) == false, "checkIsFinite(INFINITY) failed";
assert checkIsFinite(0.0) == true, "checkIsFinite(0.0) failed";
assert checkIsFinite(1) == true, "checkIsFinite(1) failed";
}
// test checkIsInfinite
{
assert checkIsInfinite(NAN) == false, "checkIsInfinite(NAN) failed";
assert checkIsInfinite(INFINITY) == true, "checkIsInfinite(INFINITY) failed";
assert checkIsInfinite(0.0) == false, "checkIsInfinite(0.0) failed";
assert checkIsInfinite(1) == false, "checkIsInfinite(1) failed";
}
// test epsilionCompare
{
assert epsilionCompare(1, 1) == true, "epsilionCompare(1, 1) failed";
assert epsilionCompare(1, 1.000001) == true, "epsilionCompare(1, 1.000001) failed";
assert epsilionCompare(1, 1.001) == false, "epsilionCompare(1, 1.001) failed";
assert epsilionCompare(0, 0) == true, "epsilionCompare(0, 0) failed";
}
+57
View File
@@ -101,6 +101,63 @@ import standard;
}
//test sign
{
assert sign(4) == 1, "sign(int) failed";
assert sign(-4) == -1, "sign(-int) failed";
assert sign(4.1) == 1, "sign(float) failed";
assert sign(-4.1) == -1, "sign(-float) failed";
assert sign(0) == 1, "sign(0) failed";
var x = 4.1;
assert x.sign() == 1, "var.sign() failed";
assert typeof sign(1.0) == int, "typeof sign() == int failed";
}
//test normalize
{
assert normalize(4) == 1, "normalize(int) failed";
assert normalize(-4) == -1, "normalize(-int) failed";
assert normalize(4.1) == 1, "normalize(float) failed";
assert normalize(-4.1) == -1, "normalize(-float) failed";
assert normalize(0) == 0, "normalize(0) failed";
var x = 4.1;
assert x.normalize() == 1, "var.normalize() failed";
assert typeof normalize(1.0) == int, "typeof normalize() == int failed";
}
//test clamp
{
assert clamp(1, 0, 5) == 1, "clamp(1, 0, 5) failed";
assert clamp(0, 1, 5) == 1, "clamp(0, 1, 5) failed";
assert clamp(10, 1, 5) == 5, "clamp(10, 1, 5) failed";
assert clamp(1.0, 0.0, 5.0) == 1, "clamp(1.0, 0.0, 5.0) failed";
assert clamp(0.0, 1.0, 5.0) == 1, "clamp(0.0, 1.0, 5.0) failed";
assert clamp(10.0, 1.0, 5.0) == 5, "clamp(10.0, 1.0, 5.0) failed";
assert typeof clamp(10, 1, 5) == int, "typeof clamp(10, 1, 5) == int failed";
assert typeof clamp(10.0, 1, 5) == int, "typeof clamp(10.0, 1, 5) == int failed";
assert typeof clamp(10, 1, 5.0) == float, "typeof clamp(10, 1, 5.0) == float failed";
}
//test lerp
{
assert lerp(0, 10, 0.5) == 5, "lerp 50% failed";
assert lerp(0, 10, 1.5) == 15, "lerp 150% failed";
assert typeof lerp(0, 10, 0) == float, "typeof lerp result failed";
}
//test concat
{
//test array concat
@@ -1,5 +1,5 @@
import about as about;
import about;
import toy_version_info as toy_version_info;
import toy_version_info;
assert author == "Kayne Ruse, KR Game Studios", "Author failed";
+14
View File
@@ -0,0 +1,14 @@
//These operators should short-circuit
assert (true && false) == false, "(true && false) == false failed";
assert (false && true) == false, "(false && true) == false failed";
assert (true || false) == true, "(true || false) == true failed";
assert (false || true) == true, "(false || true) == true failed";
//make sure the right value is being returned when chained
assert "a" && "b" && "c" == "c", "chained && failed";
assert "a" || "b" || "c" == "a", "chained || failed";
print "All good";
@@ -1,9 +0,0 @@
//explicitly support && and || short circuits
assert 1 && 2 == 2, "&& short-circuit failed";
assert 1 || 2 == 1, "|| short-circuit failed";
print "All good";
+8
View File
@@ -0,0 +1,8 @@
var array = [
1, 2, 3,
4, 5, 6,
7, 8, 9, //explicitly leave a trailing comma
];
print "All good";
+79
View File
@@ -0,0 +1,79 @@
#include "drive_system.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <string.h>
int main() {
{
//test init and quit
Toy_initDriveSystem();
Toy_freeDriveSystem();
}
{
//setup
Toy_initDriveSystem();
//test storing a value as a drive
Toy_setDrivePath("drive", "folder");
//cleanup
Toy_freeDriveSystem();
}
{
//setup
Toy_initDriveSystem();
//create a dummy interpreter (only needed for the error output function)
Toy_Interpreter interpreter;
Toy_initInterpreter(&interpreter);
//prerequisite
Toy_setDrivePath("drive", "path/to/drive");
//create the argument literal
Toy_Literal argumentLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("drive:/path/to/file"));
//test retrieving a relative path, as a literal, from the drive system
Toy_Literal resultLiteral = Toy_getDrivePathLiteral(&interpreter, &argumentLiteral);
//assert the correct value was returned
const char* cstring = Toy_toCString(TOY_AS_STRING(resultLiteral));
if (strcmp(cstring, "path/to/drive/path/to/file") != 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Incorrect value retrieved from drive system: %s" TOY_CC_RESET, cstring);
return -1;
}
//cleanup
Toy_freeLiteral(argumentLiteral);
Toy_freeLiteral(resultLiteral);
Toy_freeInterpreter(&interpreter);
Toy_freeDriveSystem();
}
{
//setup
Toy_initDriveSystem();
//test storing enough drives to trigger internal dictionary expansion
Toy_setDrivePath("A", "folder");
Toy_setDrivePath("B", "folder");
Toy_setDrivePath("C", "folder");
Toy_setDrivePath("D", "folder");
Toy_setDrivePath("E", "folder");
Toy_setDrivePath("F", "folder");
Toy_setDrivePath("G", "folder");
Toy_setDrivePath("H", "folder");
Toy_setDrivePath("I", "folder");
Toy_setDrivePath("J", "folder");
//cleanup
Toy_freeDriveSystem();
}
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
return 0;
}
+11 -3
View File
@@ -18,6 +18,7 @@ static void noPrintFn(const char* output) {
//NO OP
}
int failedAssertions = 0;
int ignoredAssertions = 0;
static void noAssertFn(const char* output) {
if (strncmp(output, "!ignore", 7) == 0) {
@@ -27,6 +28,7 @@ static void noAssertFn(const char* output) {
fprintf(stderr, TOY_CC_ERROR "Assertion failure: ");
fprintf(stderr, "%s", output);
fprintf(stderr, "\n" TOY_CC_RESET); //default new line
failedAssertions++;
}
}
@@ -119,6 +121,7 @@ int main() {
"dottify-bugfix.toy",
"function-within-function-bugfix.toy",
"functions.toy",
"group-casting-bugfix.toy",
"increment-postfix-bugfix.toy",
"index-arrays.toy",
"index-assignment-both-bugfix.toy",
@@ -126,6 +129,7 @@ int main() {
"index-assignment-left-bugfix.toy",
"index-dictionaries.toy",
"index-strings.toy",
"indexing-in-argument-list-bugfix.toy",
"jumps.toy",
"jumps-in-functions.toy",
"logicals.toy",
@@ -137,8 +141,9 @@ int main() {
"panic-within-functions.toy",
"polyfill-insert.toy",
"polyfill-remove.toy",
"short-circuiting-support.toy",
"short-circuit.toy",
"ternary-expressions.toy",
"trailing-comma-bugfix.toy",
"types.toy",
NULL
};
@@ -159,7 +164,10 @@ int main() {
return -1;
}
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
return 0;
if (failedAssertions == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
return failedAssertions;
}
+6 -4
View File
@@ -6,18 +6,19 @@
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_drive_system.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../repl/repl_tools.h"
#include "../repl/drive_system.h"
#include "../repl/lib_about.h"
#include "../repl/lib_toy_version_info.h"
#include "../repl/lib_standard.h"
#include "../repl/lib_random.h"
#include "../repl/lib_runner.h"
#include "../repl/lib_standard.h"
#include "../repl/lib_math.h"
//supress the print output
static void noPrintFn(const char* output) {
@@ -72,10 +73,11 @@ int main() {
//run each file in test/scripts
Payload payloads[] = {
{"interactions.toy", "standard", Toy_hookStandard}, //interactions needs standard
{"about.toy", "about", Toy_hookAbout},
{"toy_version_info.toy", "toy_version_info", Toy_hookToyVersionInfo},
{"standard.toy", "standard", Toy_hookStandard},
{"runner.toy", "runner", Toy_hookRunner},
{"random.toy", "random", Toy_hookRandom},
{"math.toy", "math", Toy_hookMath},
{NULL, NULL, NULL}
};
BIN
View File
Binary file not shown.
+550
View File
@@ -0,0 +1,550 @@
/*
* disassembler.c
*
* Created on: 10 ago. 2023
* Original Author: egonzalez
*
* Further modified by Kayne Ruse, and added to the Toy Programming Language tool repository.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "disassembler.h"
#define SPC(n) printf("%.*s", n, "| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |");
#define EP(x) [x] = #x
const char *OP_STR[] = {
EP(DIS_OP_EOF), //
EP(DIS_OP_PASS), //
EP(DIS_OP_ASSERT), //
EP(DIS_OP_PRINT), //
EP(DIS_OP_LITERAL), //
EP(DIS_OP_LITERAL_LONG), //
EP(DIS_OP_LITERAL_RAW), //
EP(DIS_OP_NEGATE), //
EP(DIS_OP_ADDITION), //
EP(DIS_OP_SUBTRACTION), //
EP(DIS_OP_MULTIPLICATION), //
EP(DIS_OP_DIVISION), //
EP(DIS_OP_MODULO), //
EP(DIS_OP_GROUPING_BEGIN), //
EP(DIS_OP_GROUPING_END), //
EP(DIS_OP_SCOPE_BEGIN), //
EP(DIS_OP_SCOPE_END), //
EP(DIS_OP_TYPE_DECL_removed), //
EP(DIS_OP_TYPE_DECL_LONG_removed), //
EP(DIS_OP_VAR_DECL), //
EP(DIS_OP_VAR_DECL_LONG), //
EP(DIS_OP_FN_DECL), //
EP(DIS_OP_FN_DECL_LONG), //
EP(DIS_OP_VAR_ASSIGN), //
EP(DIS_OP_VAR_ADDITION_ASSIGN), //
EP(DIS_OP_VAR_SUBTRACTION_ASSIGN), //
EP(DIS_OP_VAR_MULTIPLICATION_ASSIGN), //
EP(DIS_OP_VAR_DIVISION_ASSIGN), //
EP(DIS_OP_VAR_MODULO_ASSIGN), //
EP(DIS_OP_TYPE_CAST), //
EP(DIS_OP_TYPE_OF), //
EP(DIS_OP_IMPORT), //
EP(DIS_OP_EXPORT_removed), //
EP(DIS_OP_INDEX), //
EP(DIS_OP_INDEX_ASSIGN), //
EP(DIS_OP_INDEX_ASSIGN_INTERMEDIATE), //
EP(DIS_OP_DOT), //
EP(DIS_OP_COMPARE_EQUAL), //
EP(DIS_OP_COMPARE_NOT_EQUAL), //
EP(DIS_OP_COMPARE_LESS), //
EP(DIS_OP_COMPARE_LESS_EQUAL), //
EP(DIS_OP_COMPARE_GREATER), //
EP(DIS_OP_COMPARE_GREATER_EQUAL), //
EP(DIS_OP_INVERT), //
EP(DIS_OP_AND), //
EP(DIS_OP_OR), //
EP(DIS_OP_JUMP), //
EP(DIS_OP_IF_FALSE_JUMP), //
EP(DIS_OP_FN_CALL), //
EP(DIS_OP_FN_RETURN), //
EP(DIS_OP_POP_STACK), //
EP(DIS_OP_TERNARY), //
EP(DIS_OP_FN_END), //
};
const char *LIT_STR[] = {
EP(DIS_LITERAL_NULL), //
EP(DIS_LITERAL_BOOLEAN), //
EP(DIS_LITERAL_INTEGER), //
EP(DIS_LITERAL_FLOAT), //
EP(DIS_LITERAL_STRING), //
EP(DIS_LITERAL_ARRAY), //
EP(DIS_LITERAL_DICTIONARY), //
EP(DIS_LITERAL_FUNCTION), //
EP(DIS_LITERAL_IDENTIFIER), //
EP(DIS_LITERAL_TYPE), //
EP(DIS_LITERAL_OPAQUE), //
EP(DIS_LITERAL_ANY), //
EP(DIS_LITERAL_TYPE_INTERMEDIATE), //
EP(DIS_LITERAL_ARRAY_INTERMEDIATE), //
EP(DIS_LITERAL_DICTIONARY_INTERMEDIATE), //
EP(DIS_LITERAL_FUNCTION_INTERMEDIATE), //
EP(DIS_LITERAL_FUNCTION_ARG_REST), //
EP(DIS_LITERAL_FUNCTION_NATIVE), //
EP(DIS_LITERAL_FUNCTION_HOOK), //
EP(DIS_LITERAL_INDEX_BLANK), //
};
enum DIS_ARG_TYPE {
DIS_ARG_NONE, //
DIS_ARG_BYTE, //
DIS_ARG_WORD, //
DIS_ARG_INTEGER, //
DIS_ARG_FLOAT, //
DIS_ARG_STRING //
};
const uint8_t OP_ARGS[DIS_OP_END_OPCODES][2] = {
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_EOF
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_PASS
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_ASSERT
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_PRINT
{ DIS_ARG_BYTE, DIS_ARG_NONE }, // DIS_OP_LITERAL
{ DIS_ARG_WORD, DIS_ARG_NONE }, // DIS_OP_LITERAL_LONG
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_LITERAL_RAW
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_NEGATE
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_ADDITION
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_SUBTRACTION
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_MULTIPLICATION
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_DIVISION
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_MODULO
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_GROUPING_BEGIN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_GROUPING_END
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_SCOPE_BEGIN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_SCOPE_END
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_TYPE_DECL_removed
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_TYPE_DECL_LONG_removed
{ DIS_ARG_BYTE, DIS_ARG_BYTE }, // DIS_OP_VAR_DECL
{ DIS_ARG_WORD, DIS_ARG_WORD }, // DIS_OP_VAR_DECL_LONG
{ DIS_ARG_BYTE, DIS_ARG_BYTE }, // DIS_OP_FN_DECL
{ DIS_ARG_WORD, DIS_ARG_WORD }, // DIS_OP_FN_DECL_LONG
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_VAR_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_VAR_ADDITION_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_VAR_SUBTRACTION_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_VAR_MULTIPLICATION_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_VAR_DIVISION_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_VAR_MODULO_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_TYPE_CAST
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_TYPE_OF
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_IMPORT
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_EXPORT_removed
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_INDEX
{ DIS_ARG_BYTE, DIS_ARG_NONE }, // DIS_OP_INDEX_ASSIGN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_INDEX_ASSIGN_INTERMEDIATE
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_DOT
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_COMPARE_EQUAL
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_COMPARE_NOT_EQUAL
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_COMPARE_LESS
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_COMPARE_LESS_EQUAL
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_COMPARE_GREATER
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_COMPARE_GREATER_EQUAL
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_INVERT
{ DIS_ARG_WORD, DIS_ARG_NONE }, // DIS_OP_AND
{ DIS_ARG_WORD, DIS_ARG_NONE }, // DIS_OP_OR
{ DIS_ARG_WORD, DIS_ARG_NONE }, // DIS_OP_JUMP
{ DIS_ARG_WORD, DIS_ARG_NONE }, // DIS_OP_IF_FALSE_JUMP
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_FN_CALL
{ DIS_ARG_WORD, DIS_ARG_NONE }, // DIS_OP_FN_RETURN
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_POP_STACK
{ DIS_ARG_NONE, DIS_ARG_NONE }, // DIS_OP_TERNARY
};
typedef struct dis_program_s {
uint8_t *program;
uint32_t len;
uint32_t pc;
} dis_program_t;
typedef struct dis_func_op_s {
uint32_t start;
uint32_t end;
} dis_func_op_t;
static void dis_print_opcode(uint8_t op);
static uint8_t readByte(const uint8_t *tb, uint32_t *count) {
uint8_t ret = *(uint8_t*) (tb + *count);
*count += 1;
return ret;
}
static uint16_t readWord(const uint8_t *tb, uint32_t *count) {
uint16_t ret = 0;
memcpy(&ret, tb + *count, 2);
*count += 2;
return ret;
}
static int32_t readInt(const uint8_t *tb, uint32_t *count) {
int ret = 0;
memcpy(&ret, tb + *count, 4);
*count += 4;
return ret;
}
static float readFloat(const uint8_t *tb, uint32_t *count) {
float ret = 0;
memcpy(&ret, tb + *count, 4);
*count += 4;
return ret;
}
static char* readString(const uint8_t *tb, uint32_t *count) {
const unsigned char *ret = tb + *count;
*count += strlen((char*) ret) + 1; //+1 for null character
return (char*) ret;
}
static void consumeByte(uint8_t byte, uint8_t *tb, uint32_t *count) {
if (byte != tb[*count]) {
printf("[internal] Failed to consume the correct byte (expected %u, found %u)\n", byte, tb[*count]);
exit(1);
}
*count += 1;
}
///////////////////////////////////////////////////////////////////////////////
static void dis_disassembler_init(dis_program_t **prg) {
(*prg) = malloc(sizeof(struct dis_program_s));
(*prg)->len = 0;
(*prg)->pc = 0;
}
static void dis_disassembler_deinit(dis_program_t **prg) {
free((*prg)->program);
free((*prg));
}
static uint8_t dis_load_file(const char *filename, dis_program_t **prg) {
FILE *f;
size_t fsize, bytes;
uint32_t count = 0;
uint8_t buf = 0;
f = fopen(filename, "r");
if (f == NULL) {
printf("Not able to open the file.\n");
return 1;
}
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fseek(f, 0, SEEK_SET);
(*prg)->program = malloc(fsize * sizeof(uint8_t));
while ((bytes = fread(&buf, sizeof(uint8_t), 1, f)) == 1)
(*prg)->program[count++] = buf;
(*prg)->len = fsize;
printf("\nFile: %s\nSize: %zu\n", filename, fsize);
fclose(f);
return 0;
}
static void dis_read_header(dis_program_t **prg) {
const unsigned char major = readByte((*prg)->program, &((*prg)->pc));
const unsigned char minor = readByte((*prg)->program, &((*prg)->pc));
const unsigned char patch = readByte((*prg)->program, &((*prg)->pc));
const char *build = readString((*prg)->program, &((*prg)->pc));
printf("[Header Version: %d.%d.%d (%s)]\n", major, minor, patch, build);
}
static void dis_print_opcode(uint8_t op) {
if (op == 255) {
printf("SECTION_END");
return;
}
if (op < DIS_OP_END_OPCODES)
printf("%s", (OP_STR[op] + 7));
else
printf("(OP UNKNOWN [%c])", op);
}
///////////////////////////////////////////////////////////////////////////////
#define S_OP(n) \
switch (OP_ARGS[opcode][n]) { \
case DIS_ARG_NONE: \
break; \
case DIS_ARG_BYTE: \
uint = readByte((*prg)->program, &pc); \
printf(" b(%d)", uint); \
break; \
case DIS_ARG_WORD: \
uint = readWord((*prg)->program, &pc);\
printf(" w(%d)", uint); \
break; \
case DIS_ARG_INTEGER: \
intg = readInt((*prg)->program, &pc); \
printf(" i(%d)", intg); \
break; \
case DIS_ARG_FLOAT: \
flt = readFloat((*prg)->program, &pc); \
printf(" f(%f)", flt); \
break; \
case DIS_ARG_STRING: \
str = readString((*prg)->program, &pc); \
printf(" s(%s)", str); \
break; \
default: \
printf("ERROR, unknown argument type\n"); \
exit(1); \
}
static void dis_disassemble_section(dis_program_t **prg, uint32_t pc, uint32_t len, uint8_t spaces, bool is_function) {
uint8_t opcode;
uint32_t uint;
int32_t intg;
float flt;
char *str;
//first 4 bytes of the program section within a function are actually specifying the parameter and return lists
if (is_function) {
printf("\n");
uint16_t args = readWord((*prg)->program, &pc);
uint16_t rets = readWord((*prg)->program, &pc);
SPC(spaces);
printf("| [args literal %d, rets literal %d]", args, rets);
}
while (pc < len) {
opcode = (*prg)->program[pc];
printf("\n");
SPC(spaces);
printf("| [ %05d ](%03d) ", pc++, opcode);
dis_print_opcode(opcode);
if (opcode > DIS_OP_END_OPCODES)
continue;
S_OP(0);
S_OP(1);
}
}
#define LIT_ADD(a, b, c) b[c] = a; ++c;
static void dis_read_interpreter_sections(dis_program_t **prg, uint32_t *pc, uint8_t spaces) {
uint32_t literal_count = 0;
uint8_t literal_type[65536];
const unsigned short literalCount = readWord((*prg)->program, pc);
printf("\n");
SPC(spaces);
printf("| ( Reading %d literals )\n", literalCount);
for (int i = 0; i < literalCount; i++) {
const unsigned char literalType = readByte((*prg)->program, pc);
switch (literalType) {
case DIS_LITERAL_NULL:
LIT_ADD(DIS_LITERAL_NULL, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( null )\n", i);
break;
case DIS_LITERAL_BOOLEAN: {
const bool b = readByte((*prg)->program, pc);
LIT_ADD(DIS_LITERAL_BOOLEAN, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( boolean %s )\n", i, b ? "true" : "false");
}
break;
case DIS_LITERAL_INTEGER: {
const int d = readInt((*prg)->program, pc);
LIT_ADD(DIS_LITERAL_INTEGER, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( integer %d )\n", i, d);
}
break;
case DIS_LITERAL_FLOAT: {
const float f = readFloat((*prg)->program, pc);
LIT_ADD(DIS_LITERAL_FLOAT, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( float %f )\n", i, f);
}
break;
case DIS_LITERAL_STRING: {
const char *s = readString((*prg)->program, pc);
LIT_ADD(DIS_LITERAL_STRING, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( string \"%s\" )\n", i, s);
}
break;
case DIS_LITERAL_ARRAY_INTERMEDIATE:
case DIS_LITERAL_ARRAY: {
unsigned short length = readWord((*prg)->program, pc);
SPC(spaces);
printf("| | [%d] ( array ", i);
for (int i = 0; i < length; i++) {
int index = readWord((*prg)->program, pc);
printf("%d ", index);
LIT_ADD(DIS_LITERAL_NULL, literal_type, literal_count);
if (!(i % 15) && i != 0) {
printf("\n");
SPC(spaces);
printf("| | ");
}
}
printf(")\n");
LIT_ADD(DIS_LITERAL_ARRAY, literal_type, literal_count);
}
break;
case DIS_LITERAL_DICTIONARY_INTERMEDIATE:
case DIS_LITERAL_DICTIONARY: {
unsigned short length = readWord((*prg)->program, pc);
SPC(spaces);
printf("| | [%d] ( dictionary ", i);
for (int i = 0; i < length / 2; i++) {
int key = readWord((*prg)->program, pc);
int val = readWord((*prg)->program, pc);
printf("(key: %d, val:%d) ", key, val);
if(!(i % 5) && i != 0){
printf("\n");
SPC(spaces);
printf("| | ");
}
}
printf(")\n");
LIT_ADD(DIS_LITERAL_DICTIONARY, literal_type, literal_count);
}
break;
case DIS_LITERAL_FUNCTION: {
unsigned short index = readWord((*prg)->program, pc);
LIT_ADD(DIS_LITERAL_FUNCTION_INTERMEDIATE, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( function index: %d )\n", i, index);
}
break;
case DIS_LITERAL_IDENTIFIER: {
const char *str = readString((*prg)->program, pc);
LIT_ADD(DIS_LITERAL_IDENTIFIER, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( identifier %s )\n", i, str);
}
break;
case DIS_LITERAL_TYPE:
case DIS_LITERAL_TYPE_INTERMEDIATE: {
uint8_t literalType = readByte((*prg)->program, pc);
uint8_t constant = readByte((*prg)->program, pc);
SPC(spaces);
printf("| | [%d] ( type %s: %d)\n", i, (LIT_STR[literalType] + 12), constant);
if (literalType == DIS_LITERAL_ARRAY) {
uint16_t vt = readWord((*prg)->program, pc);
SPC(spaces);
printf("| | | ( subtype: %d)\n", vt);
}
if (literalType == DIS_LITERAL_DICTIONARY) {
uint8_t kt = readWord((*prg)->program, pc);
uint8_t vt = readWord((*prg)->program, pc);
SPC(spaces);
printf("| | | ( subtype: [%d, %d] )\n", kt, vt);
}
LIT_ADD(literalType, literal_type, literal_count);
}
break;
case DIS_LITERAL_INDEX_BLANK:
LIT_ADD(DIS_LITERAL_INDEX_BLANK, literal_type, literal_count);
SPC(spaces);
printf("| | [%d] ( blank )\n", i);
break;
}
}
consumeByte(DIS_OP_SECTION_END, (*prg)->program, pc);
int functionCount = readWord((*prg)->program, pc);
int functionSize = readWord((*prg)->program, pc);
if (functionCount) {
SPC(spaces);
printf("| | \n");
SPC(spaces);
printf("| | ( fun count: %d, total size: %d )\n", functionCount, functionSize);
uint32_t fcnt = 0;
for (uint32_t i = 0; i < literal_count; i++) {
if (literal_type[i] == DIS_LITERAL_FUNCTION_INTERMEDIATE) {
size_t size = (size_t) readWord((*prg)->program, pc);
uint32_t fpc_start = *pc;
uint32_t fpc_end = *pc + size - 1;
SPC(spaces);
printf("| | |\n");
SPC(spaces);
printf("| | | ( fun %d [ start: %d, end: %d ] )", fcnt, fpc_start, fpc_end);
if ((*prg)->program[*pc + size - 1] != DIS_OP_FN_END) {
printf("\nERROR: Failed to find function end\n");
exit(1);
}
dis_read_interpreter_sections(prg, &fpc_start, spaces + 4);
SPC(spaces);
printf("| | |\n");
SPC(spaces + 4);
printf("| ------ CODE ------");
dis_disassemble_section(prg, fpc_start, fpc_end, spaces + 4, true);
printf("\n");
SPC(spaces + 4);
printf("| ---- END CODE ----\n");
fcnt++;
*pc += size;
}
}
}
consumeByte(DIS_OP_SECTION_END, (*prg)->program, pc);
}
///////////////////////////////////////////////////////////////////////////////
void disassemble(const char *filename) {
dis_program_t *prg;
dis_disassembler_init(&prg);
if (dis_load_file(filename, &prg))
exit(1);
dis_read_header(&prg);
consumeByte(DIS_OP_SECTION_END, prg->program, &(prg->pc));
printf("\n| ---- LITERALS ----");
dis_read_interpreter_sections(&prg, &(prg->pc), 0);
printf("| -- END LITERALS --\n|");
printf("\n| ---- PROGRAM ----");
dis_disassemble_section(&prg, prg->pc, prg->len, 0, false);
printf("\n| -- END PROGRAM --");
printf("\n\n");
dis_disassembler_deinit(&prg);
}
+119
View File
@@ -0,0 +1,119 @@
#ifndef DISASSEMBLER_H_
#define DISASSEMBLER_H_
typedef enum DIS_OPCODES {
DIS_OP_EOF, //
// do nothing
DIS_OP_PASS, //
// basic statements
DIS_OP_ASSERT, //
DIS_OP_PRINT, //
// data
DIS_OP_LITERAL, //
DIS_OP_LITERAL_LONG, // for more than 256 literals in a chunk
DIS_OP_LITERAL_RAW, // forcibly get the raw value of the literal
// arithmetic operators
DIS_OP_NEGATE, //
DIS_OP_ADDITION, //
DIS_OP_SUBTRACTION, //
DIS_OP_MULTIPLICATION, //
DIS_OP_DIVISION, //
DIS_OP_MODULO, //
DIS_OP_GROUPING_BEGIN, //
DIS_OP_GROUPING_END, //
// variable stuff
DIS_OP_SCOPE_BEGIN, //
DIS_OP_SCOPE_END, //
DIS_OP_TYPE_DECL_removed, // deprecated
DIS_OP_TYPE_DECL_LONG_removed, // deprecated
DIS_OP_VAR_DECL, // declare a variable to be used (as a literal)
DIS_OP_VAR_DECL_LONG, // declare a variable to be used (as a long literal)
DIS_OP_FN_DECL, // declare a function to be used (as a literal)
DIS_OP_FN_DECL_LONG, // declare a function to be used (as a long literal)
DIS_OP_VAR_ASSIGN, // assign to a literal
DIS_OP_VAR_ADDITION_ASSIGN, //
DIS_OP_VAR_SUBTRACTION_ASSIGN, //
DIS_OP_VAR_MULTIPLICATION_ASSIGN, //
DIS_OP_VAR_DIVISION_ASSIGN, //
DIS_OP_VAR_MODULO_ASSIGN, //
DIS_OP_TYPE_CAST, // temporarily change a type of an atomic value
DIS_OP_TYPE_OF, // get the type of a variable
DIS_OP_IMPORT, //
DIS_OP_EXPORT_removed, // deprecated
// for indexing
DIS_OP_INDEX, //
DIS_OP_INDEX_ASSIGN, //
DIS_OP_INDEX_ASSIGN_INTERMEDIATE, //
DIS_OP_DOT, //
// comparison of values
DIS_OP_COMPARE_EQUAL, //
DIS_OP_COMPARE_NOT_EQUAL, //
DIS_OP_COMPARE_LESS, //
DIS_OP_COMPARE_LESS_EQUAL, //
DIS_OP_COMPARE_GREATER, //
DIS_OP_COMPARE_GREATER_EQUAL, //
DIS_OP_INVERT, // for booleans
// logical operators
DIS_OP_AND, //
DIS_OP_OR, //
// jumps, and conditional jumps (absolute)
DIS_OP_JUMP, //
DIS_OP_IF_FALSE_JUMP, //
DIS_OP_FN_CALL, //
DIS_OP_FN_RETURN, //
// pop the stack at the end of a complex statement
DIS_OP_POP_STACK, //
//ternary shorthand
DIS_OP_TERNARY, //
//meta
DIS_OP_FN_END, // different from SECTION_END
DIS_OP_END_OPCODES, // mark for end opcodes list. Not valid opcode
DIS_OP_SECTION_END = 255,
} dis_opcode_t;
typedef enum DIS_LITERAL_TYPE {
DIS_LITERAL_NULL, //
DIS_LITERAL_BOOLEAN, //
DIS_LITERAL_INTEGER, //
DIS_LITERAL_FLOAT, //
DIS_LITERAL_STRING, //
DIS_LITERAL_ARRAY, //
DIS_LITERAL_DICTIONARY, //
DIS_LITERAL_FUNCTION, //
DIS_LITERAL_IDENTIFIER, //
DIS_LITERAL_TYPE, //
DIS_LITERAL_OPAQUE, //
DIS_LITERAL_ANY, //
// these are meta-level types - not for general use
DIS_LITERAL_TYPE_INTERMEDIATE, // used to process types in the compiler only
DIS_LITERAL_ARRAY_INTERMEDIATE, // used to process arrays in the compiler only
DIS_LITERAL_DICTIONARY_INTERMEDIATE, // used to process dictionaries in the compiler only
DIS_LITERAL_FUNCTION_INTERMEDIATE, // used to process functions in the compiler only
DIS_LITERAL_FUNCTION_ARG_REST, // used to process function rest parameters only
DIS_LITERAL_FUNCTION_NATIVE, // for handling native functions only
DIS_LITERAL_FUNCTION_HOOK, // for handling hook functions within literals only
DIS_LITERAL_INDEX_BLANK, // for blank indexing i.e. arr[:]
} dis_literal_type_t;
extern void disassemble(const char *filename);
#endif /* DISASSEMBLER_H_ */
+8
View File
@@ -0,0 +1,8 @@
#include <stdlib.h>
#include "disassembler.h"
int main(int argc, const char* argv[]) {
disassemble(argv[1]);
return EXIT_SUCCESS;
}
+27
View File
@@ -0,0 +1,27 @@
CC=gcc
IDIR+=.
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=
ODIR = obj
SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUTNAME=
OUT=disassembler
all: $(OBJ)
$(CC) $(CFLAGS) -o $(OUT) $(OBJ) $(LIBS)
$(OBJ): | $(ODIR)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) -r $(ODIR)
+73
View File
@@ -0,0 +1,73 @@
//changing of the guard - made for @hyperiondev's microcontrollers
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
std::string convert(std::string str) {
str = str.substr(str.find_last_of("\\/")+1);
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
std::replace(str.begin(), str.end(), '.', '_');
return str;
}
std::string convertToGuardStart(std::string str) {
str = convert(str);
return "#ifndef " + str + "\n#define " + str + "\n";
}
std::string convertToGuardEnd(std::string str) {
str = convert(str);
return "\n#endif //" + str + "\n";
}
int main(int argc, char** argv) {
if (argc <= 1) {
return -1;
}
for (int fileCounter = 1; fileCounter < argc; fileCounter++) {
std::ifstream is; //input stream
std::string buffer; //store output file
//open
is.open(argv[fileCounter]);
if (!is.is_open()) {
return -1;
}
while (!is.eof()) {
std::string top; //I dislike C++
getline(is, top);
//check for pragma guard
if (top == "#pragma once") {
top = convertToGuardStart(argv[fileCounter]);
getline(is, buffer, '\0');
buffer += convertToGuardEnd(argv[fileCounter]);
}
else {
top += "\n";
getline(is, buffer, '\0');
}
buffer = top + buffer;
}
//finally
is.close();
std::ofstream os;
os.open(argv[fileCounter]);
if (!os.is_open()) {
return -1;
}
os << buffer;
os.close();
}
return 0;
}
+105
View File
@@ -0,0 +1,105 @@
//MECHA: Markdown Embedded Comment Heuristic Analyzer
/*!
# Handy-Dandy *MECHA* Docs!
The following is the source code for MECHA - a tool for reading in source code, and splicing out the markdown embedded within. It can also spit out chunks of code, though that's a bit uglier.
In theory, this should work correctly on all languages that conform to C-style comments, including Toy.
To build it, simply run `g++ -o mecha mecha.cpp`.
To run the result, you must pass in a series of filenames via the command line.
## License
This tool is considered part of Toy's toolchain, so Toy's zlib license should cover it.
## Header Files
If you're reading this as a comment in the source code, this is what I mean by ugly. Thankfully, this section is outputted correctly in markdown.
!*/
//*!```
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
//!*
/*!```
# Recursive Docs
If you run mecha on it's own source code, it should Just Work, producing a file called mecha_cpp.md
!*/
int main(int argc, char** argv) {
if (argc <= 1) {
return -1;
}
for (int fileCounter = 1; fileCounter < argc; fileCounter++) {
std::ifstream is; //input stream
std::string buffer; //store output file
//open
is.open(argv[fileCounter]);
if (!is.is_open()) {
return -1;
}
while (!is.eof()) {
//skip until correct characters found
if (is.get() != '*') {
continue;
}
if (is.get() != '!') {
continue;
}
//found the start of the block - begin reading markdown content
while (!is.eof()) {
char c = is.get();
if (c == '!' && is.peek() == '*') {
break;
}
buffer += c;
}
//bugfix
if (buffer.length() >= 2 && buffer.substr(buffer.length()-2) == "//") {
buffer.pop_back();
buffer.pop_back();
}
}
//finally
is.close();
if (buffer.length() == 0) {
continue;
}
std::ofstream os;
std::string ofname = argv[fileCounter];
std::replace(ofname.begin(), ofname.end(), '.', '_');
ofname += ".md";
os.open(ofname);
if (!os.is_open()) {
return -1;
}
os << buffer;
os.close();
}
return 0;
}