Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b602e2ff87 | |||
| 0b99eb7b0c | |||
| 2505cedc79 | |||
| d7035a59c8 | |||
| ea584d8950 | |||
| 2ce9a0cf42 | |||
| b77f0fb50d | |||
| edb5a52562 | |||
| 9dc9316853 | |||
| a864a1a226 | |||
| c645026620 | |||
| a9ccd65da1 | |||
| 0da5201829 | |||
| 6be29ed8c5 | |||
| 6341d3337f | |||
| d4f952eafc | |||
| d5bc07d3b3 | |||
| 5a851f6fbe | |||
| d8c6a3ec27 | |||
| b5883e248b | |||
| 52048f2466 | |||
| 6b8e95d250 | |||
| 5721edc2d1 | |||
| db52c13613 | |||
| 7290efe069 | |||
| 0cf92bdeae | |||
| 4c9a2e5378 | |||
| 1e11e9eea7 | |||
| cce8ae1ea3 | |||
| ce54912232 | |||
| 23b55fc360 | |||
| 62fe86f99b | |||
| 401de578a5 | |||
| fb4258f9df | |||
| f885fdaf4c | |||
| cfec1b6911 | |||
| a63bdaef1c | |||
| 3783c94064 | |||
| d292b33184 | |||
| 64944c24f6 | |||
| 604604e8bc | |||
| 67e49b7477 | |||
| 967963c9d7 | |||
| 9b469e6eb0 | |||
| f8094fa17e | |||
| 8714c56c3e | |||
| 9faaa311e0 | |||
| f5ba1181c0 | |||
| b06b2d9485 | |||
| e3e9ca7ece | |||
| 81fe278c96 | |||
| 027d093e21 | |||
| 2eaf7fc71a | |||
| c43310f316 | |||
| 6e07c5f2f4 | |||
| 7690dce3f6 | |||
| 1ed1993489 | |||
| 9b5327b83d | |||
| 10dbe8f8f1 | |||
| 9e4ad7a9a5 | |||
| 5317a12383 | |||
| 35bfa1b9f1 | |||
| 7f692b4cb4 | |||
| 0cef0abdb5 | |||
| 6ba42b5a9b | |||
| 3cb62274c9 | |||
| 60b561d809 | |||
| 70b2dcd829 | |||
| 0955b3ff38 | |||
| 4137935468 | |||
| ebeabcb9d4 | |||
| 4d33a9473a | |||
| 61e3cdba82 | |||
| 3b7d2be87e | |||
| fa175203c9 | |||
| b4a3e9b42b | |||
| 6347778ead | |||
| ba98624e82 | |||
| 24ce965e76 | |||
| 29c130135c | |||
| 01eca37560 | |||
| 8e5ec7d847 | |||
| 9ed6383630 | |||
| 1ec0f63f76 | |||
| d2341ae227 | |||
| 3782f2aaaa | |||
| b636ab9e31 | |||
| cdfe17ad53 | |||
| 3d7d1179c9 | |||
| c3c46b4fc6 | |||
| 0e41b00ef4 | |||
| f6ec6a8c73 | |||
| 2157b2f540 | |||
| 1481216e69 | |||
| f25f389b4e | |||
| deff784df8 | |||
| 54e82846c3 | |||
| 67fce427eb | |||
| 8a2cb61435 | |||
| 50d03e28fc | |||
| 763581c73b | |||
| cdb2613e5d | |||
| 733df87c08 | |||
| bfd506f497 | |||
| 18b59c9e84 | |||
| d3eb31d964 | |||
| 07f4a98b95 | |||
| 0949fd6ff9 | |||
| 03e5096f10 | |||
| bb81b8c474 | |||
| cf6db57787 | |||
| 17f0e4476b | |||
| 1095e1a885 | |||
| 2edfbbe3ef | |||
| 4b83f1f0d6 | |||
| e2fa1cf2e8 | |||
| a04d2c4816 | |||
| f2f8aed23a | |||
| 68ed52b347 | |||
| 88dac53ae0 | |||
| f84cdff883 | |||
| f869c9425a | |||
| 76ddd5703e | |||
| 669808730e | |||
| e6d9809da5 | |||
| 502032e514 | |||
| 6e9d42f892 | |||
| 70ca27486e | |||
| 12fa434e0f | |||
| efc1e764d2 | |||
| c5c0122243 | |||
| 348b7b8c24 | |||
| e243ad949a | |||
| 9b673f23ad | |||
| 624a0c80ba | |||
| 1064b69d04 | |||
| e9b347acb6 | |||
| 071c8da2aa | |||
| d6538812bf | |||
| 3aeddff736 | |||
| c88c1b125d | |||
| 1513ba9878 | |||
| bc0289c3f4 | |||
| 92c71a374d | |||
| e0547474b8 | |||
| 3e6d21afbb | |||
| d3df01c1c4 | |||
| cdca6fa45c | |||
| 1dde9d8f29 | |||
| 7f0f17b6e0 | |||
| 3507104121 | |||
| 87de634e30 | |||
| 6fa224fa7b | |||
| 8a68d864e6 | |||
| 49f240ea07 | |||
| 3acbd7447a | |||
| 6f126e6daa | |||
| 2adb9d9158 | |||
| 1668dca255 | |||
| 501ff6fff4 | |||
| 3845627fe5 | |||
| cdae03bd54 | |||
| 7b501b71b5 | |||
| 913738a4d1 | |||
| 3312a38c7c | |||
| 71b57fd42c | |||
| 453afbab41 |
@@ -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,17 +1,26 @@
|
|||||||
name: Comprehensive Tests
|
name: Continuous Integration v1.x
|
||||||
|
|
||||||
|
#trigger when these occur
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "dev" ]
|
branches:
|
||||||
|
- v1
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
branches:
|
||||||
|
- v1
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
#testing the CI workflows under multiple supported conditions
|
||||||
jobs:
|
jobs:
|
||||||
test-valgrind:
|
test-valgrind:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: install valgrind
|
- name: install valgrind
|
||||||
run: sudo apt install valgrind
|
run: sudo apt install valgrind
|
||||||
- name: make test (valgrind)
|
- name: make test (valgrind)
|
||||||
@@ -21,7 +30,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: make test (sanitized)
|
- name: make test (sanitized)
|
||||||
run: make test-sanitized
|
run: make test-sanitized
|
||||||
|
|
||||||
@@ -29,6 +38,6 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: make test (mingw32)
|
- name: make test (mingw32)
|
||||||
run: make test
|
run: make test
|
||||||
+56
-29
@@ -1,32 +1,59 @@
|
|||||||
#Editor generated files
|
# Prerequisites
|
||||||
*.suo
|
*.d
|
||||||
*.ncb
|
|
||||||
*.user
|
|
||||||
compile_commands.json
|
|
||||||
|
|
||||||
#Directories
|
# Object files
|
||||||
Release/
|
|
||||||
Debug/
|
|
||||||
Out/
|
|
||||||
release/
|
|
||||||
debug/
|
|
||||||
out/
|
|
||||||
bin/
|
|
||||||
.cache/
|
|
||||||
.vs/
|
|
||||||
|
|
||||||
#Project generated files
|
|
||||||
*.db
|
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.ko
|
||||||
*.exe
|
*.obj
|
||||||
*.meta
|
*.elf
|
||||||
*.log
|
|
||||||
*.out
|
|
||||||
*.stackdump
|
|
||||||
*.tb
|
|
||||||
*.filters
|
|
||||||
|
|
||||||
#Shell files
|
# Linker output
|
||||||
*.bat
|
*.ilk
|
||||||
*.sh
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
||||||
|
.cproject
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
temp/
|
||||||
|
Release/
|
||||||
|
out/
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# License
|
# License
|
||||||
|
|
||||||
Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios
|
Copyright (c) 2020-2024 Kayne Ruse, KR Game Studios
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,21 @@
|
|||||||
<image src="toylogo.png" />
|
<image src="toylogo.png" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Toy
|
# Toy v1
|
||||||
|
|
||||||
|
The Toy programming language is an imperative bytecode-intermediate embedded scripting language. It isn't intended to operate on its own, but rather as part of another program, the "host". This process is intended to allow a decent amount of easy customisation by the host's end user, by exposing logic in script files. Alternatively, binary files in a custom format can be used as well.
|
||||||
|
|
||||||
|
The host will provide all of the extensions needed on a case-by-case basis. Script files have the `.toy` file extension, while binary files have the `.tb` file extension.
|
||||||
|
|
||||||
This is the Toy programming language interpreter, written in C.
|
This is the Toy programming language interpreter, written in C.
|
||||||
|
|
||||||
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
|
||||||
|
|
||||||
# Nifty Features
|
# Nifty Features
|
||||||
|
|
||||||
* Simple C-like syntax
|
* Simple C-like syntax
|
||||||
* Bytecode intermediate compilation
|
* Bytecode intermediate compilation
|
||||||
* Optional, but robust type system (including `opaque` for arbitrary data)
|
* Optional, but robust type system (including `opaque` for arbitrary data)
|
||||||
* Functions and types are first-class citizens
|
* Functions and types are first-class citizens
|
||||||
* Import external libraries
|
* Import native libraries from the host
|
||||||
* Fancy slice notation for strings, arrays and dictionaries
|
* Fancy slice notation for strings, arrays and dictionaries
|
||||||
* Can re-direct output, error and assertion failure messages
|
* Can re-direct output, error and assertion failure messages
|
||||||
* Open source under the zlib license
|
* Open source under the zlib license
|
||||||
@@ -33,6 +35,8 @@ Run `make install-tools` to install a number of tools, including:
|
|||||||
|
|
||||||
* VSCode syntax highlighting
|
* VSCode syntax highlighting
|
||||||
|
|
||||||
|
Other tools such as a disassembler are available, as well - simply run `make` in the correct directory.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -67,7 +71,16 @@ print tally(); //3
|
|||||||
|
|
||||||
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
|
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
|
||||||
|
|
||||||
|
# Contributions
|
||||||
|
|
||||||
|
@hiperiondev - Disassembler, porting support and feedback
|
||||||
|
@add00 - Library support
|
||||||
|
@gruelingpine185 - Unofficial MacOS support
|
||||||
|
@solar-mist - Minor bugfixes
|
||||||
|
Unnamed Individuals - Feedback
|
||||||
|
|
||||||
# Patrons via Patreon
|
# Patrons via Patreon
|
||||||
|
|
||||||
* Seth A. Robinson
|
* Seth A. Robinson
|
||||||
|
|
||||||
|
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
||||||
|
|||||||
+34
-8
@@ -71,8 +71,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<IgnoreImportLibrary>false</IgnoreImportLibrary>
|
<IgnoreImportLibrary>false</IgnoreImportLibrary>
|
||||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
<OutDir>$(SolutionDir)out\</OutDir>
|
||||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(SolutionDir)out\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -106,25 +110,47 @@
|
|||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>$(SolutionDir)out</AdditionalLibraryDirectories>
|
||||||
</Link>
|
</Link>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>C:\Users\kayne\Desktop\Toy\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
|
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
|
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)out</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="repl\lib_about.c" />
|
<ProjectReference Include="Toy.vcxproj">
|
||||||
<ClCompile Include="repl\lib_compound.c" />
|
<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_runner.c" />
|
||||||
<ClCompile Include="repl\lib_standard.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_main.c" />
|
||||||
<ClCompile Include="repl\repl_tools.c" />
|
<ClCompile Include="repl\repl_tools.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="repl\lib_about.h" />
|
<ClInclude Include="repl\drive_system.h" />
|
||||||
<ClInclude Include="repl\lib_compound.h" />
|
<ClInclude Include="repl\lib_math.h" />
|
||||||
|
<ClInclude Include="repl\lib_random.h" />
|
||||||
<ClInclude Include="repl\lib_runner.h" />
|
<ClInclude Include="repl\lib_runner.h" />
|
||||||
<ClInclude Include="repl\lib_standard.h" />
|
<ClInclude Include="repl\lib_standard.h" />
|
||||||
|
<ClInclude Include="repl\lib_toy_version_info.h" />
|
||||||
<ClInclude Include="repl\repl_tools.h" />
|
<ClInclude Include="repl\repl_tools.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
|||||||
+39
-3
@@ -41,7 +41,7 @@
|
|||||||
<PlatformToolset>v143</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -70,8 +70,14 @@
|
|||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
<OutDir>$(SolutionDir)out\</OutDir>
|
||||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(SolutionDir)out\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -105,7 +111,34 @@
|
|||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
</ClCompile>
|
</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>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="source\toy_ast_node.c" />
|
<ClCompile Include="source\toy_ast_node.c" />
|
||||||
@@ -120,10 +153,12 @@
|
|||||||
<ClCompile Include="source\toy_literal_dictionary.c" />
|
<ClCompile Include="source\toy_literal_dictionary.c" />
|
||||||
<ClCompile Include="source\toy_memory.c" />
|
<ClCompile Include="source\toy_memory.c" />
|
||||||
<ClCompile Include="source\toy_parser.c" />
|
<ClCompile Include="source\toy_parser.c" />
|
||||||
|
<ClCompile Include="source\toy_reffunction.c" />
|
||||||
<ClCompile Include="source\toy_refstring.c" />
|
<ClCompile Include="source\toy_refstring.c" />
|
||||||
<ClCompile Include="source\toy_scope.c" />
|
<ClCompile Include="source\toy_scope.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="source\toy.h" />
|
||||||
<ClInclude Include="source\toy_ast_node.h" />
|
<ClInclude Include="source\toy_ast_node.h" />
|
||||||
<ClInclude Include="source\toy_builtin.h" />
|
<ClInclude Include="source\toy_builtin.h" />
|
||||||
<ClInclude Include="source\toy_common.h" />
|
<ClInclude Include="source\toy_common.h" />
|
||||||
@@ -138,6 +173,7 @@
|
|||||||
<ClInclude Include="source\toy_memory.h" />
|
<ClInclude Include="source\toy_memory.h" />
|
||||||
<ClInclude Include="source\toy_opcodes.h" />
|
<ClInclude Include="source\toy_opcodes.h" />
|
||||||
<ClInclude Include="source\toy_parser.h" />
|
<ClInclude Include="source\toy_parser.h" />
|
||||||
|
<ClInclude Include="source\toy_reffunction.h" />
|
||||||
<ClInclude Include="source\toy_refstring.h" />
|
<ClInclude Include="source\toy_refstring.h" />
|
||||||
<ClInclude Include="source\toy_scope.h" />
|
<ClInclude Include="source\toy_scope.h" />
|
||||||
<ClInclude Include="source\toy_token_types.h" />
|
<ClInclude Include="source\toy_token_types.h" />
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
# Optimisation Options
|
|
||||||
# export CFLAGS+=-O2 -mtune=native -march=native
|
|
||||||
# export CFLAGS+=-fsanitize=address,undefined
|
|
||||||
|
|
||||||
export CFLAGS+=-std=c18 -pedantic -Werror
|
export CFLAGS+=-std=c18 -pedantic -Werror
|
||||||
|
|
||||||
export TOY_OUTDIR = out
|
export TOY_OUTDIR = out
|
||||||
@@ -28,12 +24,16 @@ library: $(TOY_OUTDIR)
|
|||||||
static: $(TOY_OUTDIR)
|
static: $(TOY_OUTDIR)
|
||||||
$(MAKE) -j8 -C source static
|
$(MAKE) -j8 -C source static
|
||||||
|
|
||||||
library-release: $(TOY_OUTDIR)
|
library-release: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -j8 -C source library-release
|
$(MAKE) -j8 -C source library-release
|
||||||
|
|
||||||
static-release: $(TOY_OUTDIR)
|
static-release: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -j8 -C source static-release
|
$(MAKE) -j8 -C source static-release
|
||||||
|
|
||||||
|
#distribution
|
||||||
|
dist: export CFLAGS+=-O2 -mtune=native -march=native
|
||||||
|
dist: repl-release
|
||||||
|
|
||||||
#utils
|
#utils
|
||||||
test: clean $(TOY_OUTDIR)
|
test: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -C test
|
$(MAKE) -C test
|
||||||
@@ -51,6 +51,25 @@ $(TOY_OUTDIR):
|
|||||||
install-tools:
|
install-tools:
|
||||||
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
|
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
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#include "drive_system.h"
|
||||||
|
|
||||||
|
#include "toy_memory.h"
|
||||||
|
#include "toy_literal_dictionary.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//file system API
|
||||||
|
static Toy_LiteralDictionary driveDictionary;
|
||||||
|
|
||||||
|
void Toy_initDriveSystem() {
|
||||||
|
Toy_initLiteralDictionary(&driveDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_freeDriveSystem() {
|
||||||
|
Toy_freeLiteralDictionary(&driveDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_setDrivePath(char* drive, char* path) {
|
||||||
|
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
|
||||||
|
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
|
||||||
|
|
||||||
|
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
|
||||||
|
|
||||||
|
Toy_freeLiteral(driveLiteral);
|
||||||
|
Toy_freeLiteral(pathLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
|
||||||
|
//check argument types
|
||||||
|
if (!TOY_IS_STRING(*drivePathLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
|
||||||
|
|
||||||
|
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||||
|
size_t driveLength = 0;
|
||||||
|
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||||
|
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||||
|
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
|
||||||
|
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
driveLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||||
|
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||||
|
|
||||||
|
//get the real drive file path
|
||||||
|
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||||
|
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
|
||||||
|
|
||||||
|
if (!TOY_IS_STRING(pathLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||||
|
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
|
||||||
|
interpreter->errorOutput("\n");
|
||||||
|
Toy_freeLiteral(driveLiteral);
|
||||||
|
Toy_freeLiteral(pathLiteral);
|
||||||
|
Toy_deleteRefString(filePath);
|
||||||
|
Toy_deleteRefString(drivePath);
|
||||||
|
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||||
|
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
|
||||||
|
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
|
||||||
|
|
||||||
|
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
|
||||||
|
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
|
||||||
|
|
||||||
|
//clean up the drive/path stuff
|
||||||
|
Toy_deleteRefString(drivePath);
|
||||||
|
Toy_deleteRefString(filePath);
|
||||||
|
Toy_deleteRefString(path);
|
||||||
|
Toy_freeLiteral(driveLiteral);
|
||||||
|
Toy_freeLiteral(pathLiteral);
|
||||||
|
|
||||||
|
//check for break-out attempts
|
||||||
|
for (size_t i = 0; i < fileLength - 1; i++) {
|
||||||
|
if (file[i] == '.' && file[i + 1] == '.') {
|
||||||
|
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||||
|
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
|
||||||
|
|
||||||
|
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,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);
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
|
|
||||||
+1152
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
int Toy_hookMath(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#include "lib_random.h"
|
||||||
|
|
||||||
|
#include "toy_memory.h"
|
||||||
|
|
||||||
|
static int hashInt(int x) {
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = (x >> 16) ^ x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct Toy_RandomGenerator {
|
||||||
|
int seed; //mutated with each call
|
||||||
|
} Toy_RandomGenerator;
|
||||||
|
|
||||||
|
//Toy native functions
|
||||||
|
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the seed argument
|
||||||
|
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Toy_Literal seedLiteralIdn = seedLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
|
||||||
|
Toy_freeLiteral(seedLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TOY_IS_INTEGER(seedLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
|
||||||
|
Toy_freeLiteral(seedLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate the generator object
|
||||||
|
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
|
||||||
|
generator->seed = TOY_AS_INTEGER(seedLiteral);
|
||||||
|
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
|
||||||
|
|
||||||
|
//return and cleanup
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
|
||||||
|
|
||||||
|
Toy_freeLiteral(seedLiteral);
|
||||||
|
Toy_freeLiteral(generatorLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the runner object
|
||||||
|
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||||
|
Toy_freeLiteral(generatorLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||||
|
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||||
|
|
||||||
|
//generate the new value and package up the return
|
||||||
|
generator->seed = hashInt(generator->seed);
|
||||||
|
|
||||||
|
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeLiteral(generatorLiteral);
|
||||||
|
Toy_freeLiteral(resultLiteral);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the runner object
|
||||||
|
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||||
|
Toy_freeLiteral(generatorLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||||
|
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||||
|
|
||||||
|
//clear out the runner object
|
||||||
|
TOY_FREE(Toy_RandomGenerator, generator);
|
||||||
|
Toy_freeLiteral(generatorLiteral);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
const char* name;
|
||||||
|
Toy_NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"createRandomGenerator", nativeCreateRandomGenerator},
|
||||||
|
{"generateRandomNumber", nativeGenerateRandomNumber},
|
||||||
|
{"freeRandomGenerator", nativeFreeRandomGenerator},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!TOY_IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
Toy_freeLiteral(alias);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||||
|
Toy_initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||||
|
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||||
|
|
||||||
|
Toy_setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
Toy_freeLiteral(name);
|
||||||
|
Toy_freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||||
|
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||||
|
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeLiteral(dict);
|
||||||
|
Toy_freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
#define TOY_OPAQUE_TAG_RANDOM 200
|
||||||
|
|
||||||
|
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
+12
-146
@@ -4,8 +4,8 @@
|
|||||||
#include "toy_interpreter.h"
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
|
#include "drive_system.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
typedef struct Toy_Runner {
|
typedef struct Toy_Runner {
|
||||||
@@ -32,7 +32,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
|
|||||||
Toy_freeLiteral(drivePathLiteralIdn);
|
Toy_freeLiteral(drivePathLiteralIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral);
|
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
|
||||||
|
|
||||||
if (TOY_IS_NULL(filePathLiteral)) {
|
if (TOY_IS_NULL(filePathLiteral)) {
|
||||||
Toy_freeLiteral(filePathLiteral);
|
Toy_freeLiteral(filePathLiteral);
|
||||||
@@ -48,7 +48,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
|
|||||||
|
|
||||||
//load and compile the bytecode
|
//load and compile the bytecode
|
||||||
size_t fileSize = 0;
|
size_t fileSize = 0;
|
||||||
const char* source = Toy_readFile(filePath, &fileSize);
|
const char* source = (const char*)Toy_readFile(filePath, &fileSize);
|
||||||
|
|
||||||
if (!source) {
|
if (!source) {
|
||||||
interpreter->errorOutput("Failed to load source file\n");
|
interpreter->errorOutput("Failed to load source file\n");
|
||||||
@@ -102,70 +102,19 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
|
|||||||
Toy_freeLiteral(drivePathLiteralIdn);
|
Toy_freeLiteral(drivePathLiteralIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
|
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
|
||||||
|
|
||||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
if (TOY_IS_NULL(filePathLiteral)) {
|
||||||
size_t driveLength = 0;
|
Toy_freeLiteral(filePathLiteral);
|
||||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
|
||||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
|
||||||
interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n");
|
|
||||||
Toy_deleteRefString(drivePath);
|
|
||||||
Toy_freeLiteral(drivePathLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
driveLength++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
|
||||||
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
|
||||||
|
|
||||||
//get the real drive file path
|
|
||||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
|
||||||
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
|
|
||||||
|
|
||||||
if (!TOY_IS_STRING(realDriveLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
|
||||||
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
|
|
||||||
interpreter->errorOutput("\n");
|
|
||||||
Toy_freeLiteral(realDriveLiteral);
|
|
||||||
Toy_freeLiteral(driveLiteral);
|
|
||||||
Toy_deleteRefString(path);
|
|
||||||
Toy_deleteRefString(drivePath);
|
|
||||||
Toy_freeLiteral(drivePathLiteral);
|
Toy_freeLiteral(drivePathLiteral);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
|
||||||
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
|
|
||||||
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
|
|
||||||
|
|
||||||
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
|
|
||||||
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
|
|
||||||
|
|
||||||
//clean up the drivepath stuff
|
|
||||||
Toy_deleteRefString(realDrive);
|
|
||||||
Toy_freeLiteral(realDriveLiteral);
|
|
||||||
Toy_freeLiteral(driveLiteral);
|
|
||||||
Toy_deleteRefString(path);
|
|
||||||
Toy_deleteRefString(drivePath);
|
|
||||||
Toy_freeLiteral(drivePathLiteral);
|
Toy_freeLiteral(drivePathLiteral);
|
||||||
|
|
||||||
//check for file extensions
|
//use raw types - easier
|
||||||
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) {
|
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
|
||||||
interpreter->errorOutput("Bad binary file extension (expected .tb)\n");
|
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
|
||||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check for break-out attempts
|
|
||||||
for (size_t i = 0; i < realLength - 1; i++) {
|
|
||||||
if (filePath[i] == '.' && filePath[i + 1] == '.') {
|
|
||||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
|
||||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//load the bytecode
|
//load the bytecode
|
||||||
size_t fileSize = 0;
|
size_t fileSize = 0;
|
||||||
@@ -192,7 +141,8 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
|
|||||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||||
|
|
||||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
//free the drive path
|
||||||
|
Toy_freeLiteral(filePathLiteral);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -512,7 +462,7 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
|
|||||||
//store the library in an aliased dictionary
|
//store the library in an aliased dictionary
|
||||||
if (!TOY_IS_NULL(alias)) {
|
if (!TOY_IS_NULL(alias)) {
|
||||||
//make sure the name isn't taken
|
//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");
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
Toy_freeLiteral(alias);
|
Toy_freeLiteral(alias);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -559,87 +509,3 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//file system API
|
|
||||||
static Toy_LiteralDictionary Toy_driveDictionary;
|
|
||||||
|
|
||||||
void Toy_initDriveDictionary() {
|
|
||||||
Toy_initLiteralDictionary(&Toy_driveDictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_freeDriveDictionary() {
|
|
||||||
Toy_freeLiteralDictionary(&Toy_driveDictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_LiteralDictionary* Toy_getDriveDictionary() {
|
|
||||||
return &Toy_driveDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
|
|
||||||
//check argument types
|
|
||||||
if (!TOY_IS_STRING(*drivePathLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
|
|
||||||
return TOY_TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
|
|
||||||
|
|
||||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
|
||||||
size_t driveLength = 0;
|
|
||||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
|
||||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
|
||||||
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
|
|
||||||
|
|
||||||
return TOY_TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
driveLength++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
|
||||||
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
|
||||||
|
|
||||||
//get the real drive file path
|
|
||||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
|
||||||
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
|
|
||||||
|
|
||||||
if (!TOY_IS_STRING(realDriveLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
|
||||||
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
|
|
||||||
interpreter->errorOutput("\n");
|
|
||||||
Toy_freeLiteral(realDriveLiteral);
|
|
||||||
Toy_freeLiteral(driveLiteral);
|
|
||||||
Toy_deleteRefString(path);
|
|
||||||
Toy_deleteRefString(drivePath);
|
|
||||||
|
|
||||||
return TOY_TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
|
||||||
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
|
|
||||||
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
|
|
||||||
|
|
||||||
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
|
|
||||||
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
|
|
||||||
|
|
||||||
//clean up the drivepath stuff
|
|
||||||
Toy_deleteRefString(realDrive);
|
|
||||||
Toy_freeLiteral(realDriveLiteral);
|
|
||||||
Toy_freeLiteral(driveLiteral);
|
|
||||||
Toy_deleteRefString(path);
|
|
||||||
Toy_deleteRefString(drivePath);
|
|
||||||
|
|
||||||
//check for break-out attempts
|
|
||||||
for (size_t i = 0; i < realLength - 1; i++) {
|
|
||||||
if (filePath[i] == '.' && filePath[i + 1] == '.') {
|
|
||||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
|
||||||
TOY_FREE_ARRAY(char, filePath, realLength + 1);
|
|
||||||
return TOY_TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
|
|
||||||
|
|
||||||
TOY_FREE_ARRAY(char, filePath, realLength + 1);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
+1
-9
@@ -2,14 +2,6 @@
|
|||||||
|
|
||||||
#include "toy_interpreter.h"
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
|
|
||||||
//file system API - these need to be set by the host
|
|
||||||
void Toy_initDriveDictionary();
|
|
||||||
void Toy_freeDriveDictionary();
|
|
||||||
Toy_LiteralDictionary* Toy_getDriveDictionary();
|
|
||||||
|
|
||||||
#define TOY_OPAQUE_TAG_RUNNER 100
|
#define TOY_OPAQUE_TAG_RUNNER 100
|
||||||
|
|
||||||
//file system API - for use with other libs
|
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
|
|
||||||
|
|||||||
+610
-17
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
#include "toy_memory.h"
|
#include "toy_memory.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//no arguments
|
//no arguments
|
||||||
@@ -31,6 +32,557 @@ static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nativeHash(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to hash\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result = TOY_TO_INTEGER_LITERAL(Toy_hashLiteral(selfLiteral));
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeAbs(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to abs\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to abs\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result;
|
||||||
|
|
||||||
|
if (TOY_IS_INTEGER(selfLiteral)) {
|
||||||
|
result = TOY_TO_INTEGER_LITERAL( TOY_AS_INTEGER(selfLiteral) > 0 ? TOY_AS_INTEGER(selfLiteral) : -TOY_AS_INTEGER(selfLiteral) );
|
||||||
|
}
|
||||||
|
if (TOY_IS_FLOAT(selfLiteral)) {
|
||||||
|
result = TOY_TO_FLOAT_LITERAL( TOY_AS_FLOAT(selfLiteral) > 0 ? TOY_AS_FLOAT(selfLiteral) : -TOY_AS_FLOAT(selfLiteral) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeCeil(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to ceil\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to ceil\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result;
|
||||||
|
|
||||||
|
if (TOY_IS_INTEGER(selfLiteral)) {
|
||||||
|
//NO-OP
|
||||||
|
result = Toy_copyLiteral(selfLiteral);
|
||||||
|
}
|
||||||
|
if (TOY_IS_FLOAT(selfLiteral)) {
|
||||||
|
result = TOY_TO_INTEGER_LITERAL( (int)TOY_AS_FLOAT(selfLiteral) - TOY_AS_FLOAT(selfLiteral) == 0 ? (int)TOY_AS_FLOAT(selfLiteral) : (int)TOY_AS_FLOAT(selfLiteral) + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeFloor(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to floor\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to floor\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result;
|
||||||
|
|
||||||
|
if (TOY_IS_INTEGER(selfLiteral)) {
|
||||||
|
//NO-OP
|
||||||
|
result = Toy_copyLiteral(selfLiteral);
|
||||||
|
}
|
||||||
|
if (TOY_IS_FLOAT(selfLiteral)) {
|
||||||
|
result = TOY_TO_INTEGER_LITERAL( (int)TOY_AS_FLOAT(selfLiteral) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeMax(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//return value
|
||||||
|
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
|
||||||
|
|
||||||
|
//iterate over all arguments
|
||||||
|
do {
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to max\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if not comparing yet...
|
||||||
|
if (TOY_IS_NULL(resultLiteral)) {
|
||||||
|
resultLiteral = selfLiteral;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cooerce if needed
|
||||||
|
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
|
||||||
|
resultLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(resultLiteral) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
|
||||||
|
selfLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(selfLiteral) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//compare
|
||||||
|
if (TOY_IS_INTEGER(resultLiteral) && TOY_AS_INTEGER(resultLiteral) < TOY_AS_INTEGER(selfLiteral)) {
|
||||||
|
//NOTE: just ints, don't free
|
||||||
|
resultLiteral = selfLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (TOY_IS_FLOAT(resultLiteral) && TOY_AS_FLOAT(resultLiteral) < TOY_AS_FLOAT(selfLiteral)) {
|
||||||
|
//NOTE: just floats, don't free
|
||||||
|
resultLiteral = selfLiteral;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (arguments->count > 0);
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
Toy_freeLiteral(resultLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeMin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//return value
|
||||||
|
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
|
||||||
|
|
||||||
|
//iterate over all arguments
|
||||||
|
do {
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to min\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if not comparing yet...
|
||||||
|
if (TOY_IS_NULL(resultLiteral)) {
|
||||||
|
resultLiteral = selfLiteral;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cooerce if needed
|
||||||
|
if (TOY_IS_INTEGER(resultLiteral) && TOY_IS_FLOAT(selfLiteral)) {
|
||||||
|
resultLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(resultLiteral) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_FLOAT(resultLiteral) && TOY_IS_INTEGER(selfLiteral)) {
|
||||||
|
selfLiteral = TOY_TO_FLOAT_LITERAL( (float)TOY_AS_INTEGER(selfLiteral) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//compare
|
||||||
|
if (TOY_IS_INTEGER(resultLiteral) && TOY_AS_INTEGER(resultLiteral) > TOY_AS_INTEGER(selfLiteral)) {
|
||||||
|
//NOTE: just ints, don't free
|
||||||
|
resultLiteral = selfLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (TOY_IS_FLOAT(resultLiteral) && TOY_AS_FLOAT(resultLiteral) > TOY_AS_FLOAT(selfLiteral)) {
|
||||||
|
//NOTE: just floats, don't free
|
||||||
|
resultLiteral = selfLiteral;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (arguments->count > 0);
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
Toy_freeLiteral(resultLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeRound(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to round\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the self
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
//parse to value if needed
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(TOY_IS_INTEGER(selfLiteral) || TOY_IS_FLOAT(selfLiteral))) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to round\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result;
|
||||||
|
|
||||||
|
if (TOY_IS_INTEGER(selfLiteral)) {
|
||||||
|
//NO-OP
|
||||||
|
result = Toy_copyLiteral(selfLiteral);
|
||||||
|
}
|
||||||
|
if (TOY_IS_FLOAT(selfLiteral)) {
|
||||||
|
//catch the already-rounded case
|
||||||
|
if (TOY_AS_FLOAT(selfLiteral) == (int)TOY_AS_FLOAT(selfLiteral)) {
|
||||||
|
result = TOY_TO_INTEGER_LITERAL((int)TOY_AS_FLOAT(selfLiteral));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = TOY_TO_INTEGER_LITERAL( TOY_AS_FLOAT(selfLiteral) - (int)TOY_AS_FLOAT(selfLiteral) < 0.5 ? (int)TOY_AS_FLOAT(selfLiteral) : (int)TOY_AS_FLOAT(selfLiteral) + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
Toy_freeLiteral(result);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int 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) {
|
static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//no arguments
|
//no arguments
|
||||||
if (arguments->count != 2) {
|
if (arguments->count != 2) {
|
||||||
@@ -109,7 +661,7 @@ static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
|
|||||||
}
|
}
|
||||||
|
|
||||||
//get the combined length for the new string
|
//get the combined length for the new string
|
||||||
size_t length = TOY_AS_STRING(selfLiteral)->length + TOY_AS_STRING(otherLiteral)->length + 1;
|
size_t length = TOY_AS_STRING(selfLiteral)->length + TOY_AS_STRING(otherLiteral)->length;
|
||||||
|
|
||||||
if (length > TOY_MAX_STRING_LENGTH) {
|
if (length > TOY_MAX_STRING_LENGTH) {
|
||||||
interpreter->errorOutput("Can't concatenate these strings, result is too long (error found in concat)\n");
|
interpreter->errorOutput("Can't concatenate these strings, result is too long (error found in concat)\n");
|
||||||
@@ -119,8 +671,8 @@ static int nativeConcat(Toy_Interpreter* interpreter, Toy_LiteralArray* argument
|
|||||||
}
|
}
|
||||||
|
|
||||||
//allocate the space and generate
|
//allocate the space and generate
|
||||||
char* buffer = TOY_ALLOCATE(char, length);
|
char* buffer = TOY_ALLOCATE(char, length + 1);
|
||||||
snprintf(buffer, length, "%s%s", Toy_toCString(TOY_AS_STRING(selfLiteral)), Toy_toCString(TOY_AS_STRING(otherLiteral)));
|
snprintf(buffer, length + 1, "%s%s", Toy_toCString(TOY_AS_STRING(selfLiteral)), Toy_toCString(TOY_AS_STRING(otherLiteral)));
|
||||||
|
|
||||||
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer));
|
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer));
|
||||||
|
|
||||||
@@ -1066,8 +1618,13 @@ static void recursiveLiteralQuicksortUtil(Toy_Interpreter* interpreter, Toy_Lite
|
|||||||
swapLiteralsUtil(&ptr[runner], &ptr[literalCount - 1]);
|
swapLiteralsUtil(&ptr[runner], &ptr[literalCount - 1]);
|
||||||
|
|
||||||
//recurse on each end
|
//recurse on each end
|
||||||
recursiveLiteralQuicksortUtil(interpreter, &ptr[0], runner, fnCompareLiteral);
|
if (runner > 0) {
|
||||||
recursiveLiteralQuicksortUtil(interpreter, &ptr[runner + 1], literalCount - runner - 1, fnCompareLiteral);
|
recursiveLiteralQuicksortUtil(interpreter, &ptr[0], runner, fnCompareLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runner < literalCount) {
|
||||||
|
recursiveLiteralQuicksortUtil(interpreter, &ptr[runner + 1], literalCount - runner - 1, fnCompareLiteral);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
@@ -1100,8 +1657,34 @@ static int nativeSort(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BUGFIX: check if the array is already sorted
|
||||||
|
bool sorted = true;
|
||||||
|
for (int checker = 0; checker < TOY_AS_ARRAY(selfLiteral)->count - 1 && sorted; checker++) {
|
||||||
|
Toy_LiteralArray arguments;
|
||||||
|
Toy_LiteralArray returns;
|
||||||
|
|
||||||
|
Toy_initLiteralArray(&arguments);
|
||||||
|
Toy_initLiteralArray(&returns);
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[checker]);
|
||||||
|
Toy_pushLiteralArray(&arguments, TOY_AS_ARRAY(selfLiteral)->literals[checker + 1]);
|
||||||
|
|
||||||
|
Toy_callLiteralFn(interpreter, fnLiteral, &arguments, &returns);
|
||||||
|
|
||||||
|
Toy_Literal lessThan = Toy_popLiteralArray(&returns);
|
||||||
|
|
||||||
|
Toy_freeLiteralArray(&arguments);
|
||||||
|
Toy_freeLiteralArray(&returns);
|
||||||
|
|
||||||
|
if (!TOY_IS_TRUTHY(lessThan)) {
|
||||||
|
sorted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_freeLiteral(lessThan);
|
||||||
|
}
|
||||||
|
|
||||||
//call the quicksort util
|
//call the quicksort util
|
||||||
if (TOY_IS_ARRAY(selfLiteral)) {
|
if (!sorted) {
|
||||||
recursiveLiteralQuicksortUtil(interpreter, TOY_AS_ARRAY(selfLiteral)->literals, TOY_AS_ARRAY(selfLiteral)->count, fnLiteral);
|
recursiveLiteralQuicksortUtil(interpreter, TOY_AS_ARRAY(selfLiteral)->literals, TOY_AS_ARRAY(selfLiteral)->count, fnLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1189,12 +1772,6 @@ static int nativeToString(Toy_Interpreter* interpreter, Toy_LiteralArray* argume
|
|||||||
Toy_freeLiteral(selfLiteralIdn);
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: probably an undefined variable
|
|
||||||
if (TOY_IS_IDENTIFIER(selfLiteral)) {
|
|
||||||
Toy_freeLiteral(selfLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//print it to a custom function
|
//print it to a custom function
|
||||||
Toy_printLiteralCustom(selfLiteral, toStringUtil);
|
Toy_printLiteralCustom(selfLiteral, toStringUtil);
|
||||||
|
|
||||||
@@ -1288,7 +1865,7 @@ static int nativeTrim(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(selfLiteralIdn);
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_STRING(selfLiteral)) {
|
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
|
||||||
interpreter->errorOutput("Incorrect argument type passed to trim\n");
|
interpreter->errorOutput("Incorrect argument type passed to trim\n");
|
||||||
Toy_freeLiteral(trimCharsLiteral);
|
Toy_freeLiteral(trimCharsLiteral);
|
||||||
Toy_freeLiteral(selfLiteral);
|
Toy_freeLiteral(selfLiteral);
|
||||||
@@ -1399,7 +1976,7 @@ static int nativeTrimBegin(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
|
|||||||
Toy_freeLiteral(selfLiteralIdn);
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_STRING(selfLiteral)) {
|
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
|
||||||
interpreter->errorOutput("Incorrect argument type passed to trimBegin\n");
|
interpreter->errorOutput("Incorrect argument type passed to trimBegin\n");
|
||||||
Toy_freeLiteral(trimCharsLiteral);
|
Toy_freeLiteral(trimCharsLiteral);
|
||||||
Toy_freeLiteral(selfLiteral);
|
Toy_freeLiteral(selfLiteral);
|
||||||
@@ -1487,7 +2064,7 @@ static int nativeTrimEnd(Toy_Interpreter* interpreter, Toy_LiteralArray* argumen
|
|||||||
Toy_freeLiteral(selfLiteralIdn);
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_STRING(selfLiteral)) {
|
if (!TOY_IS_STRING(selfLiteral) || !TOY_IS_STRING(trimCharsLiteral)) {
|
||||||
interpreter->errorOutput("Incorrect argument type passed to trimEnd\n");
|
interpreter->errorOutput("Incorrect argument type passed to trimEnd\n");
|
||||||
Toy_freeLiteral(trimCharsLiteral);
|
Toy_freeLiteral(trimCharsLiteral);
|
||||||
Toy_freeLiteral(selfLiteral);
|
Toy_freeLiteral(selfLiteral);
|
||||||
@@ -1556,7 +2133,23 @@ typedef struct Natives {
|
|||||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||||
//build the natives list
|
//build the natives list
|
||||||
Natives natives[] = {
|
Natives natives[] = {
|
||||||
|
//misc. utils
|
||||||
{"clock", nativeClock},
|
{"clock", nativeClock},
|
||||||
|
{"hash", nativeHash},
|
||||||
|
|
||||||
|
//math utils
|
||||||
|
{"abs", nativeAbs},
|
||||||
|
{"ceil", nativeCeil},
|
||||||
|
{"floor", nativeFloor},
|
||||||
|
{"max", nativeMax},
|
||||||
|
{"min", nativeMin},
|
||||||
|
{"round", nativeRound},
|
||||||
|
{"sign", nativeSign},
|
||||||
|
{"normalize", nativeNormalize},
|
||||||
|
{"clamp", nativeClamp},
|
||||||
|
{"lerp", nativeLerp},
|
||||||
|
|
||||||
|
//compound utils
|
||||||
{"concat", nativeConcat}, //array, dictionary, string
|
{"concat", nativeConcat}, //array, dictionary, string
|
||||||
{"containsKey", nativeContainsKey}, //dictionary
|
{"containsKey", nativeContainsKey}, //dictionary
|
||||||
{"containsValue", nativeContainsValue}, //array, dictionary
|
{"containsValue", nativeContainsValue}, //array, dictionary
|
||||||
@@ -1582,7 +2175,7 @@ int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L
|
|||||||
//store the library in an aliased dictionary
|
//store the library in an aliased dictionary
|
||||||
if (!TOY_IS_NULL(alias)) {
|
if (!TOY_IS_NULL(alias)) {
|
||||||
//make sure the name isn't taken
|
//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");
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
Toy_freeLiteral(alias);
|
Toy_freeLiteral(alias);
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -3,4 +3,3 @@
|
|||||||
#include "toy_interpreter.h"
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
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"
|
#include "toy_memory.h"
|
||||||
|
|
||||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||||
//the about keys
|
//the info keys
|
||||||
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
|
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
|
||||||
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
|
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
|
||||||
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
|
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
|
||||||
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
|
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
|
||||||
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
|
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 majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
|
||||||
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
|
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
|
||||||
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
|
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
|
||||||
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
|
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
|
||||||
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
|
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 majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
|
||||||
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
|
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
|
||||||
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
|
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
|
//store as an aliased dictionary
|
||||||
if (!TOY_IS_NULL(alias)) {
|
if (!TOY_IS_NULL(alias)) {
|
||||||
//make sure the name isn't taken
|
//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");
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
Toy_freeLiteral(alias);
|
Toy_freeLiteral(alias);
|
||||||
|
|
||||||
@@ -83,11 +83,11 @@ int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
|
|||||||
//store globally
|
//store globally
|
||||||
else {
|
else {
|
||||||
//make sure the names aren't taken
|
//make sure the names aren't taken
|
||||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
|
if (Toy_isDeclaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
|
||||||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
|
Toy_isDeclaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
|
||||||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
|
Toy_isDeclaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
|
||||||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
|
Toy_isDeclaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
|
||||||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
|
Toy_isDeclaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
|
||||||
interpreter->errorOutput("Can't override an existing variable\n");
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
Toy_freeLiteral(alias);
|
Toy_freeLiteral(alias);
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
+1
-1
@@ -2,7 +2,7 @@ CC=gcc
|
|||||||
|
|
||||||
IDIR+=. ../source
|
IDIR+=. ../source
|
||||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||||
LIBS+=-ltoy
|
LIBS+=-ltoy -lm
|
||||||
|
|
||||||
ODIR = obj
|
ODIR = obj
|
||||||
SRC = $(wildcard *.c)
|
SRC = $(wildcard *.c)
|
||||||
|
|||||||
+26
-25
@@ -1,14 +1,14 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
#include "lib_about.h"
|
#include "drive_system.h"
|
||||||
|
#include "lib_toy_version_info.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
|
#include "lib_random.h"
|
||||||
#include "lib_runner.h"
|
#include "lib_runner.h"
|
||||||
|
#include "lib_math.h"
|
||||||
|
|
||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
#include "toy_lexer.h"
|
#include "toy.h"
|
||||||
#include "toy_parser.h"
|
|
||||||
#include "toy_compiler.h"
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -27,9 +27,11 @@ void repl(const char* initialInput) {
|
|||||||
Toy_initInterpreter(&interpreter);
|
Toy_initInterpreter(&interpreter);
|
||||||
|
|
||||||
//inject the libs
|
//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, "standard", Toy_hookStandard);
|
||||||
|
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||||
|
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
if (!initialInput) {
|
if (!initialInput) {
|
||||||
@@ -75,7 +77,7 @@ void repl(const char* initialInput) {
|
|||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
//get the bytecode dump
|
//get the bytecode dump
|
||||||
int size = 0;
|
size_t size = 0;
|
||||||
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
//run the bytecode
|
//run the bytecode
|
||||||
@@ -104,16 +106,9 @@ void repl(const char* initialInput) {
|
|||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
Toy_initCommandLine(argc, argv);
|
Toy_initCommandLine(argc, argv);
|
||||||
|
|
||||||
//lib setup (hacky - only really for this program)
|
//setup the drive system (for filesystem access)
|
||||||
Toy_initDriveDictionary();
|
Toy_initDriveSystem();
|
||||||
|
Toy_setDrivePath("scripts", "scripts");
|
||||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
|
||||||
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
|
||||||
|
|
||||||
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
|
|
||||||
|
|
||||||
Toy_freeLiteral(driveLiteral);
|
|
||||||
Toy_freeLiteral(pathLiteral);
|
|
||||||
|
|
||||||
//command line specific actions
|
//command line specific actions
|
||||||
if (Toy_commandLine.error) {
|
if (Toy_commandLine.error) {
|
||||||
@@ -149,7 +144,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
Toy_runSourceFile(Toy_commandLine.sourcefile);
|
Toy_runSourceFile(Toy_commandLine.sourcefile);
|
||||||
|
|
||||||
//lib cleanup
|
//lib cleanup
|
||||||
Toy_freeDriveDictionary();
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -159,7 +154,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
Toy_runSource(Toy_commandLine.source);
|
Toy_runSource(Toy_commandLine.source);
|
||||||
|
|
||||||
//lib cleanup
|
//lib cleanup
|
||||||
Toy_freeDriveDictionary();
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -180,7 +175,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
|
|
||||||
//compile and save
|
//compile and save
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const char* source = Toy_readFile(Toy_commandLine.compilefile, &size);
|
const char* source = (const char*)Toy_readFile(Toy_commandLine.compilefile, &size);
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -201,11 +196,17 @@ int main(int argc, const char* argv[]) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//run the binary file
|
if (Toy_commandLine.parseBytecodeHeader) {
|
||||||
Toy_runBinaryFile(Toy_commandLine.binaryfile);
|
//only parse the bytecode header
|
||||||
|
Toy_parseBinaryFileHeader(Toy_commandLine.binaryfile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//run the binary file
|
||||||
|
Toy_runBinaryFile(Toy_commandLine.binaryfile);
|
||||||
|
}
|
||||||
|
|
||||||
//lib cleanup
|
//lib cleanup
|
||||||
Toy_freeDriveDictionary();
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -220,13 +221,13 @@ int main(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t size;
|
size_t size;
|
||||||
initialSource = Toy_readFile(Toy_commandLine.initialfile, &size);
|
initialSource = (const char*)Toy_readFile(Toy_commandLine.initialfile, &size);
|
||||||
}
|
}
|
||||||
|
|
||||||
repl(initialSource);
|
repl(initialSource);
|
||||||
|
|
||||||
//lib cleanup
|
//lib cleanup
|
||||||
Toy_freeDriveDictionary();
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-11
@@ -1,7 +1,9 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
#include "lib_about.h"
|
#include "lib_toy_version_info.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
|
#include "lib_random.h"
|
||||||
#include "lib_runner.h"
|
#include "lib_runner.h"
|
||||||
|
#include "lib_math.h"
|
||||||
|
|
||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
@@ -14,7 +16,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
//IO functions
|
//IO functions
|
||||||
const char* Toy_readFile(const char* path, size_t* fileSize) {
|
const unsigned char* Toy_readFile(const char* path, size_t* fileSize) {
|
||||||
FILE* file = fopen(path, "rb");
|
FILE* file = fopen(path, "rb");
|
||||||
|
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
@@ -26,14 +28,14 @@ const char* Toy_readFile(const char* path, size_t* fileSize) {
|
|||||||
*fileSize = ftell(file);
|
*fileSize = ftell(file);
|
||||||
rewind(file);
|
rewind(file);
|
||||||
|
|
||||||
char* buffer = (char*)malloc(*fileSize + 1);
|
unsigned char* buffer = (unsigned char*)malloc(*fileSize + 1);
|
||||||
|
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
size_t bytesRead = fread(buffer, sizeof(unsigned char), *fileSize, file);
|
||||||
|
|
||||||
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||||
|
|
||||||
@@ -77,10 +79,10 @@ const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
|||||||
Toy_initParser(&parser, &lexer);
|
Toy_initParser(&parser, &lexer);
|
||||||
Toy_initCompiler(&compiler);
|
Toy_initCompiler(&compiler);
|
||||||
|
|
||||||
//run the parser until the end of the source
|
//step 1 - run the parser until the end of the source
|
||||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||||
while(node != NULL) {
|
while(node != NULL) {
|
||||||
//pack up and leave
|
//on error, pack up and leave
|
||||||
if (node->type == TOY_AST_NODE_ERROR) {
|
if (node->type == TOY_AST_NODE_ERROR) {
|
||||||
Toy_freeASTNode(node);
|
Toy_freeASTNode(node);
|
||||||
Toy_freeCompiler(&compiler);
|
Toy_freeCompiler(&compiler);
|
||||||
@@ -93,8 +95,8 @@ const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
|||||||
node = Toy_scanParser(&parser);
|
node = Toy_scanParser(&parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the bytecode dump
|
//step 2 - get the bytecode dump
|
||||||
const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
Toy_freeCompiler(&compiler);
|
Toy_freeCompiler(&compiler);
|
||||||
@@ -110,9 +112,11 @@ void Toy_runBinary(const unsigned char* tb, size_t size) {
|
|||||||
Toy_initInterpreter(&interpreter);
|
Toy_initInterpreter(&interpreter);
|
||||||
|
|
||||||
//inject the libs
|
//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, "standard", Toy_hookStandard);
|
||||||
|
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||||
|
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
|
||||||
|
|
||||||
Toy_runInterpreter(&interpreter, tb, (int)size);
|
Toy_runInterpreter(&interpreter, tb, (int)size);
|
||||||
Toy_freeInterpreter(&interpreter);
|
Toy_freeInterpreter(&interpreter);
|
||||||
@@ -120,7 +124,7 @@ void Toy_runBinary(const unsigned char* tb, size_t size) {
|
|||||||
|
|
||||||
void Toy_runBinaryFile(const char* fname) {
|
void Toy_runBinaryFile(const char* fname) {
|
||||||
size_t size = 0; //not used
|
size_t size = 0; //not used
|
||||||
const unsigned char* tb = (const unsigned char*)Toy_readFile(fname, &size);
|
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||||
if (!tb) {
|
if (!tb) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -140,10 +144,66 @@ void Toy_runSource(const char* source) {
|
|||||||
|
|
||||||
void Toy_runSourceFile(const char* fname) {
|
void Toy_runSourceFile(const char* fname) {
|
||||||
size_t size = 0; //not used
|
size_t size = 0; //not used
|
||||||
const char* source = Toy_readFile(fname, &size);
|
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Toy_runSource(source);
|
Toy_runSource(source);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//utils for debugging the header
|
||||||
|
static unsigned char readByte(const unsigned char* tb, int* count) {
|
||||||
|
unsigned char ret = *(unsigned char*)(tb + *count);
|
||||||
|
*count += 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* readString(const unsigned char* tb, int* count) {
|
||||||
|
const unsigned char* ret = tb + *count;
|
||||||
|
*count += (int)strlen((char*)ret) + 1; //+1 for null character
|
||||||
|
return (const char*)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_parseBinaryFileHeader(const char* fname) {
|
||||||
|
size_t size = 0; //not used
|
||||||
|
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||||
|
if (!tb || size < 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
//header section
|
||||||
|
const unsigned char major = readByte(tb, &count);
|
||||||
|
const unsigned char minor = readByte(tb, &count);
|
||||||
|
const unsigned char patch = readByte(tb, &count);
|
||||||
|
|
||||||
|
const char* build = readString(tb, &count);
|
||||||
|
|
||||||
|
printf("Toy Programming Language Interpreter Version %d.%d.%d (interpreter built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||||
|
|
||||||
|
printf("Toy Programming Language Bytecode Version ");
|
||||||
|
|
||||||
|
//print the output
|
||||||
|
if (major == TOY_VERSION_MAJOR && minor == TOY_VERSION_MINOR && patch == TOY_VERSION_PATCH) {
|
||||||
|
printf("%d.%d.%d", major, minor, patch);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%d.%d.%d" TOY_CC_RESET, major, minor, patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" (interpreter built on ");
|
||||||
|
|
||||||
|
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD)) == 0) {
|
||||||
|
printf("%s", build);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%s" TOY_CC_RESET, build);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(")\n");
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
free((void*)tb);
|
||||||
|
}
|
||||||
+71
-1
@@ -1,14 +1,84 @@
|
|||||||
#pragma once
|
#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"
|
#include "toy_common.h"
|
||||||
|
|
||||||
const char* Toy_readFile(const char* path, size_t* fileSize);
|
/*!
|
||||||
|
## 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);
|
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);
|
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_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_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_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_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);
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
import node;
|
|
||||||
|
|
||||||
//constants
|
|
||||||
var SPEED: int const = 10;
|
|
||||||
|
|
||||||
//variables
|
|
||||||
var parent: opaque = null;
|
|
||||||
var posX: int = 50;
|
|
||||||
var posY: int = 50;
|
|
||||||
var WIDTH: int const = 100;
|
|
||||||
var HEIGHT: int const = 100;
|
|
||||||
|
|
||||||
var xspeed: int = 0;
|
|
||||||
var yspeed: int = 0;
|
|
||||||
|
|
||||||
//accessors - variables are private, functions are public
|
|
||||||
fn getX(node: opaque) {
|
|
||||||
return posX;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getY(node: opaque) {
|
|
||||||
return posY;
|
|
||||||
}
|
|
||||||
|
|
||||||
//lifecycle functions
|
|
||||||
fn onInit(node: opaque) {
|
|
||||||
print "render.toy:onInit() called\n";
|
|
||||||
|
|
||||||
node.loadTexture("sprites:/character.png");
|
|
||||||
parent = node.getNodeParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onStep(node: opaque) {
|
|
||||||
posX += xspeed;
|
|
||||||
posY += yspeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onFree(node: opaque) {
|
|
||||||
print "render.toy:onFree() called\n";
|
|
||||||
|
|
||||||
node.freeTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onDraw(node: opaque) {
|
|
||||||
// print "render.toy:onDraw() called\n";
|
|
||||||
|
|
||||||
var px = parent.callNode("getX");
|
|
||||||
var py = parent.callNode("getY");
|
|
||||||
|
|
||||||
if (px == null) {
|
|
||||||
px = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (py == null) {
|
|
||||||
py = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
//event functions
|
|
||||||
fn onKeyDown(node: opaque, event: string) {
|
|
||||||
if (event == "character_up") {
|
|
||||||
yspeed -= SPEED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "character_down") {
|
|
||||||
yspeed += SPEED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "character_left") {
|
|
||||||
xspeed -= SPEED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "character_right") {
|
|
||||||
xspeed += SPEED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onKeyUp(node: opaque, event: string) {
|
|
||||||
if (event == "character_up" && yspeed < 0) {
|
|
||||||
yspeed = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "character_down" && yspeed > 0) {
|
|
||||||
yspeed = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "character_left" && xspeed < 0) {
|
|
||||||
xspeed = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "character_right" && xspeed > 0) {
|
|
||||||
xspeed = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
|
|
||||||
// print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
|
|
||||||
// print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")\n";
|
|
||||||
|
|
||||||
//jump to pos
|
|
||||||
posX = x - WIDTH / 2;
|
|
||||||
posY = y - HEIGHT / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
|
|
||||||
// print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
|
|
||||||
// print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
//single line comment
|
|
||||||
|
|
||||||
/*
|
|
||||||
multi line comment
|
|
||||||
*/
|
|
||||||
|
|
||||||
//test primitive literals
|
|
||||||
print "hello world";
|
|
||||||
print null;
|
|
||||||
print true;
|
|
||||||
print false;
|
|
||||||
print 42;
|
|
||||||
print 3.14;
|
|
||||||
print -69;
|
|
||||||
print -4.20;
|
|
||||||
print 2 + (3 * 3);
|
|
||||||
|
|
||||||
//test operators (integers)
|
|
||||||
print 1 + 1;
|
|
||||||
print 1 - 1;
|
|
||||||
print 2 * 2;
|
|
||||||
print 1 / 2;
|
|
||||||
print 4 % 2;
|
|
||||||
|
|
||||||
//test operators (floats)
|
|
||||||
print 1.0 + 1.0;
|
|
||||||
print 1.0 - 1.0;
|
|
||||||
print 2.0 * 2.0;
|
|
||||||
print 1.0 / 2.0;
|
|
||||||
|
|
||||||
//test scopes
|
|
||||||
{
|
|
||||||
print "This statement is within a scope.";
|
|
||||||
{
|
|
||||||
print "This is a deeper scope.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print "Back to the outer scope.";
|
|
||||||
|
|
||||||
//test scope will delegate to higher scope
|
|
||||||
var a = 1;
|
|
||||||
{
|
|
||||||
a = 2;
|
|
||||||
print a;
|
|
||||||
}
|
|
||||||
print a;
|
|
||||||
|
|
||||||
//test scope will shadow higher scope on redefine
|
|
||||||
var b: int = 3;
|
|
||||||
{
|
|
||||||
var b = 4;
|
|
||||||
print b;
|
|
||||||
}
|
|
||||||
print b;
|
|
||||||
|
|
||||||
//test compounds, repeatedly
|
|
||||||
print [1, 2, 3];
|
|
||||||
print [4, 5];
|
|
||||||
print ["key":"value"];
|
|
||||||
print [1, 2, 3];
|
|
||||||
print [4, 5];
|
|
||||||
print ["key":"value"];
|
|
||||||
|
|
||||||
//test empties
|
|
||||||
print [];
|
|
||||||
print [:];
|
|
||||||
|
|
||||||
//test nested compounds
|
|
||||||
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
|
||||||
|
|
||||||
//var declarations
|
|
||||||
var x = 31;
|
|
||||||
var y : int = 42;
|
|
||||||
var arr : [int] = [1, 2, 3, 42];
|
|
||||||
var dict : [string:int] = ["hello": 1, "world":2];
|
|
||||||
|
|
||||||
//printing expressions
|
|
||||||
print x;
|
|
||||||
print x + y;
|
|
||||||
print arr;
|
|
||||||
print dict;
|
|
||||||
|
|
||||||
//test asserts at the end of the file
|
|
||||||
assert x, "This won't be seen";
|
|
||||||
assert true, "This won't be seen";
|
|
||||||
assert false, "This is a failed assert, and will end execution";
|
|
||||||
|
|
||||||
print "This will not be printed because of the above assert";
|
|
||||||
|
|
||||||
@@ -17,5 +17,5 @@ fn fib(n : int) {
|
|||||||
|
|
||||||
for (var i = 0; i < 40; i++) {
|
for (var i = 0; i < 40; i++) {
|
||||||
var res = fib(i);
|
var res = fib(i);
|
||||||
print string i + ": " + string res + "\n";
|
print string i + ": " + string res;
|
||||||
}
|
}
|
||||||
+3
-2
@@ -1,9 +1,10 @@
|
|||||||
|
//WARNING: please think twice before using this in a test
|
||||||
fn fib(n : int) {
|
fn fib(n : int) {
|
||||||
if (n < 2) return n;
|
if (n < 2) return n;
|
||||||
return fib(n-1) + fib(n-2);
|
return fib(n-1) + fib(n-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 20; i++) {
|
for (var i = 0; i <= 35; i++) {
|
||||||
var res = fib(i);
|
var res = fib(i);
|
||||||
print string i + ": " + string res + "\n";
|
print string i + ": " + string res;
|
||||||
}
|
}
|
||||||
+3
-3
@@ -34,8 +34,8 @@ var tiles: [[int]] const = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
var tileset: [int: string] const = [
|
var tileset: [int: string] const = [
|
||||||
0: " ",
|
0: " ",
|
||||||
1: " X "
|
1: "X "
|
||||||
];
|
];
|
||||||
|
|
||||||
//variables
|
//variables
|
||||||
@@ -48,7 +48,7 @@ fn draw() {
|
|||||||
for (var i: int = 0; i < WIDTH; i++) {
|
for (var i: int = 0; i < WIDTH; i++) {
|
||||||
//draw the player pos
|
//draw the player pos
|
||||||
if (i == posX && j == posY) {
|
if (i == posX && j == posY) {
|
||||||
print " O ";
|
print "O ";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Since this is a pseudo-random generator, and there's no internal state to the algorithm other
|
||||||
|
than the generator opaque, there needs to be a "call counter" (current depth) to shuffle the
|
||||||
|
initial seeds, otherwise generators created from other generators will resemble their parents,
|
||||||
|
but one call greater.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import standard;
|
||||||
|
import random;
|
||||||
|
|
||||||
|
var DEPTH: int const = 20;
|
||||||
|
var levels = [];
|
||||||
|
|
||||||
|
//generate the level seeds
|
||||||
|
var generator: opaque = createRandomGenerator(clock().hash());
|
||||||
|
|
||||||
|
for (var i: int = 0; i < DEPTH; i++) {
|
||||||
|
levels.push(generator.generateRandomNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.freeRandomGenerator();
|
||||||
|
|
||||||
|
//generate "levels" of a roguelike
|
||||||
|
for (var i = 0; i < DEPTH; i++) {
|
||||||
|
var rng: opaque = createRandomGenerator(levels[i] + i);
|
||||||
|
|
||||||
|
print "---";
|
||||||
|
print levels[i];
|
||||||
|
print rng.generateRandomNumber();
|
||||||
|
print rng.generateRandomNumber();
|
||||||
|
print rng.generateRandomNumber();
|
||||||
|
|
||||||
|
rng.freeRandomGenerator();
|
||||||
|
}
|
||||||
+2
-2
@@ -29,7 +29,7 @@ for (var i = 0; i < SIZE -1; i++) {
|
|||||||
prev += " ";
|
prev += " ";
|
||||||
}
|
}
|
||||||
prev += "*"; //initial
|
prev += "*"; //initial
|
||||||
print prev + "\n";
|
print prev;
|
||||||
|
|
||||||
//run
|
//run
|
||||||
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
||||||
@@ -44,6 +44,6 @@ for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
|||||||
//right
|
//right
|
||||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||||
|
|
||||||
print output + "\n";
|
print output;
|
||||||
prev = output;
|
prev = output;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fn f() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g() {
|
||||||
|
fn i() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h() {
|
||||||
|
//
|
||||||
|
}
|
||||||
+3
-3
@@ -11,14 +11,14 @@ OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
|||||||
OUTNAME=toy
|
OUTNAME=toy
|
||||||
|
|
||||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
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
|
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||||
else ifeq ($(shell uname),Linux)
|
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
|
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
|
||||||
CFLAGS += -fPIC
|
CFLAGS += -fPIC
|
||||||
else ifeq ($(OS),Windows_NT)
|
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
|
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||||
else ifeq ($(shell uname),Darwin)
|
else ifeq ($(shell uname),Darwin)
|
||||||
LIBLINE = $(OBJ)
|
LIBLINE = $(OBJ)
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy.h - A Toy Programming Language
|
||||||
|
|
||||||
|
If you're looking how to use Toy directly, try https://toylang.com/
|
||||||
|
Otherwise, this header may help learn how Toy works internally.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Utilities
|
||||||
|
|
||||||
|
These headers define a bunch of useful macros, based on what platform you build for.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Core Pipeline
|
||||||
|
|
||||||
|
From source to execution, each step is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
source -> lexer -> token
|
||||||
|
token -> parser -> AST
|
||||||
|
AST -> compiler -> bytecode
|
||||||
|
bytecode -> interpreter -> result
|
||||||
|
```
|
||||||
|
|
||||||
|
I should note that the parser -> compiler phase is actually made up of two steps - the write step and the collate step. See `Toy_compileString()` in `repl/repl_tools.c` for an example of how to compile properly.
|
||||||
|
|
||||||
|
* [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
|
||||||
|
|
||||||
|
Literals represent any value within the language, including some internal ones that you never see.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
`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"
|
||||||
|
#include "toy_reffunction.h"
|
||||||
+45
-9
@@ -40,17 +40,21 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_AST_NODE_BLOCK:
|
case TOY_AST_NODE_BLOCK:
|
||||||
for (int i = 0; i < node->block.count; i++) {
|
if (node->block.capacity > 0) {
|
||||||
freeASTNodeCustom(node->block.nodes + i, false);
|
for (int i = 0; i < node->block.count; i++) {
|
||||||
|
freeASTNodeCustom(node->block.nodes + i, false);
|
||||||
|
}
|
||||||
|
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
|
||||||
}
|
}
|
||||||
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_AST_NODE_COMPOUND:
|
case TOY_AST_NODE_COMPOUND:
|
||||||
for (int i = 0; i < node->compound.count; i++) {
|
if (node->compound.capacity > 0) {
|
||||||
freeASTNodeCustom(node->compound.nodes + i, false);
|
for (int i = 0; i < node->compound.count; i++) {
|
||||||
|
freeASTNodeCustom(node->compound.nodes + i, false);
|
||||||
|
}
|
||||||
|
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
|
||||||
}
|
}
|
||||||
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_AST_NODE_PAIR:
|
case TOY_AST_NODE_PAIR:
|
||||||
@@ -71,10 +75,12 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_AST_NODE_FN_COLLECTION:
|
case TOY_AST_NODE_FN_COLLECTION:
|
||||||
for (int i = 0; i < node->fnCollection.count; i++) {
|
if (node->fnCollection.capacity > 0) {
|
||||||
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
for (int i = 0; i < node->fnCollection.count; i++) {
|
||||||
|
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
||||||
|
}
|
||||||
|
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
||||||
}
|
}
|
||||||
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_AST_NODE_FN_DECL:
|
case TOY_AST_NODE_FN_DECL:
|
||||||
@@ -118,6 +124,16 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
|
|||||||
//NO-OP
|
//NO-OP
|
||||||
break;
|
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:
|
case TOY_AST_NODE_PREFIX_INCREMENT:
|
||||||
Toy_freeLiteral(node->prefixIncrement.identifier);
|
Toy_freeLiteral(node->prefixIncrement.identifier);
|
||||||
break;
|
break;
|
||||||
@@ -342,6 +358,26 @@ void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
|
|||||||
*nodeHandle = tmp;
|
*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->pathAnd.left = *nodeHandle;
|
||||||
|
tmp->pathAnd.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->pathOr.left = *nodeHandle;
|
||||||
|
tmp->pathOr.right = rhs;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ typedef enum Toy_ASTNodeType {
|
|||||||
TOY_AST_NODE_FOR, //for control flow
|
TOY_AST_NODE_FOR, //for control flow
|
||||||
TOY_AST_NODE_BREAK, //for control flow
|
TOY_AST_NODE_BREAK, //for control flow
|
||||||
TOY_AST_NODE_CONTINUE, //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_PREFIX_INCREMENT, //increment a variable
|
||||||
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||||
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||||
@@ -204,6 +206,24 @@ typedef struct Toy_NodeContinue {
|
|||||||
Toy_ASTNodeType type;
|
Toy_ASTNodeType type;
|
||||||
} Toy_NodeContinue;
|
} 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
|
//pre-post increment/decrement
|
||||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||||
void Toy_emitASTNodePrefixDecrement(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_NodeFor pathFor;
|
||||||
Toy_NodeBreak pathBreak;
|
Toy_NodeBreak pathBreak;
|
||||||
Toy_NodeContinue pathContinue;
|
Toy_NodeContinue pathContinue;
|
||||||
|
Toy_NodeAnd pathAnd;
|
||||||
|
Toy_NodeOr pathOr;
|
||||||
Toy_NodePrefixIncrement prefixIncrement;
|
Toy_NodePrefixIncrement prefixIncrement;
|
||||||
Toy_NodePrefixDecrement prefixDecrement;
|
Toy_NodePrefixDecrement prefixDecrement;
|
||||||
Toy_NodePostfixIncrement postfixIncrement;
|
Toy_NodePostfixIncrement postfixIncrement;
|
||||||
@@ -270,4 +292,5 @@ union Toy_private_node {
|
|||||||
Toy_NodeImport import;
|
Toy_NodeImport import;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//see toy_parser.h for more documentation on this function
|
||||||
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
|
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
|
||||||
|
|||||||
+155
-35
@@ -4,6 +4,7 @@
|
|||||||
#include "toy_literal.h"
|
#include "toy_literal.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
//static math utils, copied from the interpreter
|
//static math utils, copied from the interpreter
|
||||||
static Toy_Literal addition(Toy_Interpreter* interpreter, Toy_Literal lhs, Toy_Literal rhs) {
|
static Toy_Literal addition(Toy_Interpreter* interpreter, Toy_Literal lhs, Toy_Literal rhs) {
|
||||||
@@ -278,6 +279,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(idn);
|
Toy_freeLiteral(idn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
|
||||||
|
Toy_freeLiteral(op);
|
||||||
|
Toy_freeLiteral(assign);
|
||||||
|
Toy_freeLiteral(third);
|
||||||
|
Toy_freeLiteral(second);
|
||||||
|
Toy_freeLiteral(first);
|
||||||
|
Toy_freeLiteral(compound);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//second and third are bad args to dictionaries
|
//second and third are bad args to dictionaries
|
||||||
if (!TOY_IS_NULL(second) || !TOY_IS_NULL(third)) {
|
if (!TOY_IS_NULL(second) || !TOY_IS_NULL(third)) {
|
||||||
interpreter->errorOutput("Index slicing not allowed for dictionaries\n");
|
interpreter->errorOutput("Index slicing not allowed for dictionaries\n");
|
||||||
@@ -400,6 +412,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(idn);
|
Toy_freeLiteral(idn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
|
||||||
|
Toy_freeLiteral(op);
|
||||||
|
Toy_freeLiteral(assign);
|
||||||
|
Toy_freeLiteral(third);
|
||||||
|
Toy_freeLiteral(second);
|
||||||
|
Toy_freeLiteral(first);
|
||||||
|
Toy_freeLiteral(compound);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//handle each error case
|
//handle each error case
|
||||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
|
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
|
||||||
interpreter->errorOutput("Bad first indexing\n");
|
interpreter->errorOutput("Bad first indexing\n");
|
||||||
@@ -542,6 +565,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(idn);
|
Toy_freeLiteral(idn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
|
||||||
|
Toy_freeLiteral(op);
|
||||||
|
Toy_freeLiteral(assign);
|
||||||
|
Toy_freeLiteral(third);
|
||||||
|
Toy_freeLiteral(second);
|
||||||
|
Toy_freeLiteral(first);
|
||||||
|
Toy_freeLiteral(compound);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//handle each error case
|
//handle each error case
|
||||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
|
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
|
||||||
interpreter->errorOutput("Bad first indexing assignment\n");
|
interpreter->errorOutput("Bad first indexing assignment\n");
|
||||||
@@ -587,7 +621,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
|
|
||||||
//simple indexing assignment if second is null
|
//simple indexing assignment if second is null
|
||||||
if (TOY_IS_NULL(second)) {
|
if (TOY_IS_NULL(second)) {
|
||||||
bool ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
if (!Toy_setLiteralArray(TOY_AS_ARRAY(compound), first, assign)) {
|
if (!Toy_setLiteralArray(TOY_AS_ARRAY(compound), first, assign)) {
|
||||||
interpreter->errorOutput("Array index out of bounds in assignment");
|
interpreter->errorOutput("Array index out of bounds in assignment");
|
||||||
@@ -595,6 +629,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Toy_pushLiteralArray(&interpreter->stack, compound); //leave the array on the stack
|
Toy_pushLiteralArray(&interpreter->stack, compound); //leave the array on the stack
|
||||||
|
//...
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,6 +739,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(idn);
|
Toy_freeLiteral(idn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(first)) {
|
||||||
|
Toy_freeLiteral(op);
|
||||||
|
Toy_freeLiteral(assign);
|
||||||
|
Toy_freeLiteral(third);
|
||||||
|
Toy_freeLiteral(second);
|
||||||
|
Toy_freeLiteral(first);
|
||||||
|
Toy_freeLiteral(compound);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Toy_Literal value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
|
Toy_Literal value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
|
||||||
|
|
||||||
if (TOY_IS_STRING(op) && Toy_equalsRefStringCString(TOY_AS_STRING(op), "+=")) {
|
if (TOY_IS_STRING(op) && Toy_equalsRefStringCString(TOY_AS_STRING(op), "+=")) {
|
||||||
@@ -792,6 +838,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(idn);
|
Toy_freeLiteral(idn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
|
||||||
|
Toy_freeLiteral(op);
|
||||||
|
Toy_freeLiteral(assign);
|
||||||
|
Toy_freeLiteral(third);
|
||||||
|
Toy_freeLiteral(second);
|
||||||
|
Toy_freeLiteral(first);
|
||||||
|
Toy_freeLiteral(compound);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//handle each error case
|
//handle each error case
|
||||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
||||||
interpreter->errorOutput("Bad first indexing in string\n");
|
interpreter->errorOutput("Bad first indexing in string\n");
|
||||||
@@ -936,6 +993,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_freeLiteral(idn);
|
Toy_freeLiteral(idn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
|
||||||
|
Toy_freeLiteral(op);
|
||||||
|
Toy_freeLiteral(assign);
|
||||||
|
Toy_freeLiteral(third);
|
||||||
|
Toy_freeLiteral(second);
|
||||||
|
Toy_freeLiteral(first);
|
||||||
|
Toy_freeLiteral(compound);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//handle each error case
|
//handle each error case
|
||||||
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
|
||||||
interpreter->errorOutput("Bad first indexing in string assignment\n");
|
interpreter->errorOutput("Bad first indexing in string assignment\n");
|
||||||
@@ -1063,7 +1131,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//if wrong number of arguments, fail
|
//if wrong number of arguments, fail
|
||||||
if (arguments->count != 3) {
|
if (arguments->count != 3) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _set\n");
|
interpreter->errorOutput("Incorrect number of arguments to set\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1073,12 +1141,16 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
Toy_Literal val = arguments->literals[2];
|
Toy_Literal val = arguments->literals[2];
|
||||||
|
|
||||||
if (!TOY_IS_IDENTIFIER(idn)) {
|
if (!TOY_IS_IDENTIFIER(idn)) {
|
||||||
interpreter->errorOutput("Expected identifier in _set\n");
|
interpreter->errorOutput("Expected identifier in set\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_parseIdentifierToValue(interpreter, &obj);
|
Toy_parseIdentifierToValue(interpreter, &obj);
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(obj)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool freeKey = false;
|
bool freeKey = false;
|
||||||
if (TOY_IS_IDENTIFIER(key)) {
|
if (TOY_IS_IDENTIFIER(key)) {
|
||||||
Toy_parseIdentifierToValue(interpreter, &key);
|
Toy_parseIdentifierToValue(interpreter, &key);
|
||||||
@@ -1091,26 +1163,40 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
freeVal = true;
|
freeVal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(key) || TOY_IS_IDENTIFIER(val)) {
|
||||||
|
if (freeKey) {
|
||||||
|
Toy_freeLiteral(key);
|
||||||
|
}
|
||||||
|
if (freeVal) {
|
||||||
|
Toy_freeLiteral(val);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
switch(obj.type) {
|
switch(obj.type) {
|
||||||
case TOY_LITERAL_ARRAY: {
|
case TOY_LITERAL_ARRAY: {
|
||||||
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, key);
|
//check the subtype of the array, if there is one, against the given argument
|
||||||
|
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, idn);
|
||||||
|
|
||||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
|
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
|
||||||
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
|
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
|
||||||
|
|
||||||
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
|
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
|
||||||
interpreter->errorOutput("Bad argument type in _set\n");
|
interpreter->errorOutput("Bad argument type in set\n");
|
||||||
|
Toy_freeLiteral(typeLiteral);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toy_freeLiteral(typeLiteral);
|
||||||
|
|
||||||
if (!TOY_IS_INTEGER(key)) {
|
if (!TOY_IS_INTEGER(key)) {
|
||||||
interpreter->errorOutput("Expected integer index in _set\n");
|
interpreter->errorOutput("Expected integer index in set\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_AS_ARRAY(obj)->count <= TOY_AS_INTEGER(key) || TOY_AS_INTEGER(key) < 0) {
|
if (TOY_AS_INTEGER(key) >= TOY_AS_ARRAY(obj)->count || TOY_AS_INTEGER(key) < 0) {
|
||||||
interpreter->errorOutput("Index out of bounds in _set\n");
|
interpreter->errorOutput("Index out of bounds in set\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1119,7 +1205,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
TOY_AS_ARRAY(obj)->literals[TOY_AS_INTEGER(key)] = Toy_copyLiteral(val);
|
TOY_AS_ARRAY(obj)->literals[TOY_AS_INTEGER(key)] = Toy_copyLiteral(val);
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
||||||
interpreter->errorOutput("Incorrect type assigned to array in _set: \"");
|
interpreter->errorOutput("Incorrect type assigned to array in set: \"");
|
||||||
Toy_printLiteralCustom(val, interpreter->errorOutput);
|
Toy_printLiteralCustom(val, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\"\n");
|
interpreter->errorOutput("\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1136,12 +1222,12 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
Toy_Literal valSubtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1];
|
Toy_Literal valSubtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1];
|
||||||
|
|
||||||
if (TOY_AS_TYPE(keySubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(keySubtypeLiteral).typeOf != key.type) {
|
if (TOY_AS_TYPE(keySubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(keySubtypeLiteral).typeOf != key.type) {
|
||||||
interpreter->printOutput("bad argument type in _set\n");
|
interpreter->printOutput("bad argument type in set\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_AS_TYPE(valSubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(valSubtypeLiteral).typeOf != val.type) {
|
if (TOY_AS_TYPE(valSubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(valSubtypeLiteral).typeOf != val.type) {
|
||||||
interpreter->printOutput("bad argument type in _set\n");
|
interpreter->printOutput("bad argument type in set\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1149,7 +1235,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
Toy_setLiteralDictionary(TOY_AS_DICTIONARY(obj), key, val);
|
Toy_setLiteralDictionary(TOY_AS_DICTIONARY(obj), key, val);
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
||||||
interpreter->errorOutput("Incorrect type assigned to dictionary in _set: \"");
|
interpreter->errorOutput("Incorrect type assigned to dictionary in set: \"");
|
||||||
Toy_printLiteralCustom(val, interpreter->errorOutput);
|
Toy_printLiteralCustom(val, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\"\n");
|
interpreter->errorOutput("\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1159,7 +1245,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interpreter->errorOutput("Incorrect compound type in _set: ");
|
interpreter->errorOutput("Incorrect compound type in set: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\"\n");
|
interpreter->errorOutput("\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1181,7 +1267,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//if wrong number of arguments, fail
|
//if wrong number of arguments, fail
|
||||||
if (arguments->count != 2) {
|
if (arguments->count != 2) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _get");
|
interpreter->errorOutput("Incorrect number of arguments to get");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1200,15 +1286,25 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
freeKey = true;
|
freeKey = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(obj) || TOY_IS_IDENTIFIER(key)) {
|
||||||
|
if (freeObj) {
|
||||||
|
Toy_freeLiteral(obj);
|
||||||
|
}
|
||||||
|
if (freeKey) {
|
||||||
|
Toy_freeLiteral(key);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
switch(obj.type) {
|
switch(obj.type) {
|
||||||
case TOY_LITERAL_ARRAY: {
|
case TOY_LITERAL_ARRAY: {
|
||||||
if (!TOY_IS_INTEGER(key)) {
|
if (!TOY_IS_INTEGER(key)) {
|
||||||
interpreter->errorOutput("Expected integer index in _get\n");
|
interpreter->errorOutput("Expected integer index in get\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_AS_ARRAY(obj)->count <= TOY_AS_INTEGER(key) || TOY_AS_INTEGER(key) < 0) {
|
if (TOY_AS_INTEGER(key) >= TOY_AS_ARRAY(obj)->count || TOY_AS_INTEGER(key) < 0) {
|
||||||
interpreter->errorOutput("Index out of bounds in _get\n");
|
interpreter->errorOutput("Index out of bounds in get\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1242,7 +1338,7 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interpreter->errorOutput("Incorrect compound type in _get \"");
|
interpreter->errorOutput("Incorrect compound type in get \"");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\"\n");
|
interpreter->errorOutput("\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1252,7 +1348,7 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//if wrong number of arguments, fail
|
//if wrong number of arguments, fail
|
||||||
if (arguments->count != 2) {
|
if (arguments->count != 2) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _push\n");
|
interpreter->errorOutput("Incorrect number of arguments to push\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1261,35 +1357,47 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_Literal val = arguments->literals[1];
|
Toy_Literal val = arguments->literals[1];
|
||||||
|
|
||||||
if (!TOY_IS_IDENTIFIER(idn)) {
|
if (!TOY_IS_IDENTIFIER(idn)) {
|
||||||
interpreter->errorOutput("Expected identifier in _push\n");
|
interpreter->errorOutput("Expected identifier in push\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_parseIdentifierToValue(interpreter, &obj);
|
Toy_parseIdentifierToValue(interpreter, &obj);
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(obj)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool freeVal = false;
|
bool freeVal = false;
|
||||||
if (TOY_IS_IDENTIFIER(val)) {
|
if (TOY_IS_IDENTIFIER(val)) {
|
||||||
Toy_parseIdentifierToValue(interpreter, &val);
|
Toy_parseIdentifierToValue(interpreter, &val);
|
||||||
freeVal = true;
|
freeVal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(val)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
switch(obj.type) {
|
switch(obj.type) {
|
||||||
case TOY_LITERAL_ARRAY: {
|
case TOY_LITERAL_ARRAY: {
|
||||||
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, val);
|
//check the subtype of the array, if there is one, against the given argument
|
||||||
|
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, idn);
|
||||||
|
|
||||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
|
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
|
||||||
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
|
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
|
||||||
|
|
||||||
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
|
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
|
||||||
interpreter->errorOutput("Bad argument type in _push");
|
interpreter->errorOutput("Bad argument type in push\n");
|
||||||
|
Toy_freeLiteral(typeLiteral);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toy_freeLiteral(typeLiteral);
|
||||||
|
|
||||||
Toy_pushLiteralArray(TOY_AS_ARRAY(obj), val);
|
Toy_pushLiteralArray(TOY_AS_ARRAY(obj), val);
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
|
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
|
||||||
interpreter->errorOutput("Incorrect type assigned to array in _push: \"");
|
interpreter->errorOutput("Incorrect type assigned to array in push: \"");
|
||||||
Toy_printLiteralCustom(val, interpreter->errorOutput);
|
Toy_printLiteralCustom(val, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\"\n");
|
interpreter->errorOutput("\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1305,7 +1413,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interpreter->errorOutput("Incorrect compound type in _push: ");
|
interpreter->errorOutput("Incorrect compound type in push: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1315,7 +1423,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//if wrong number of arguments, fail
|
//if wrong number of arguments, fail
|
||||||
if (arguments->count != 1) {
|
if (arguments->count != 1) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _pop\n");
|
interpreter->errorOutput("Incorrect number of arguments to pop\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1323,12 +1431,16 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
Toy_Literal obj = arguments->literals[0];
|
Toy_Literal obj = arguments->literals[0];
|
||||||
|
|
||||||
if (!TOY_IS_IDENTIFIER(idn)) {
|
if (!TOY_IS_IDENTIFIER(idn)) {
|
||||||
interpreter->errorOutput("Expected identifier in _pop\n");
|
interpreter->errorOutput("Expected identifier in pop\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_parseIdentifierToValue(interpreter, &obj);
|
Toy_parseIdentifierToValue(interpreter, &obj);
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(obj)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
switch(obj.type) {
|
switch(obj.type) {
|
||||||
case TOY_LITERAL_ARRAY: {
|
case TOY_LITERAL_ARRAY: {
|
||||||
Toy_Literal lit = Toy_popLiteralArray(TOY_AS_ARRAY(obj));
|
Toy_Literal lit = Toy_popLiteralArray(TOY_AS_ARRAY(obj));
|
||||||
@@ -1336,7 +1448,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
Toy_freeLiteral(lit);
|
Toy_freeLiteral(lit);
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
|
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
|
||||||
interpreter->errorOutput("Incorrect type assigned to array in _pop: ");
|
interpreter->errorOutput("Incorrect type assigned to array in pop: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1348,7 +1460,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interpreter->errorOutput("Incorrect compound type in _pop: ");
|
interpreter->errorOutput("Incorrect compound type in pop: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1358,7 +1470,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//if wrong number of arguments, fail
|
//if wrong number of arguments, fail
|
||||||
if (arguments->count != 1) {
|
if (arguments->count != 1) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _length\n");
|
interpreter->errorOutput("Incorrect number of arguments to length\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1370,6 +1482,10 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
freeObj = true;
|
freeObj = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(obj)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
switch(obj.type) {
|
switch(obj.type) {
|
||||||
case TOY_LITERAL_ARRAY: {
|
case TOY_LITERAL_ARRAY: {
|
||||||
Toy_Literal lit = TOY_TO_INTEGER_LITERAL( TOY_AS_ARRAY(obj)->count );
|
Toy_Literal lit = TOY_TO_INTEGER_LITERAL( TOY_AS_ARRAY(obj)->count );
|
||||||
@@ -1393,7 +1509,7 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interpreter->errorOutput("Incorrect compound type in _length: ");
|
interpreter->errorOutput("Incorrect compound type in length: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1409,7 +1525,7 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
//if wrong number of arguments, fail
|
//if wrong number of arguments, fail
|
||||||
if (arguments->count != 1) {
|
if (arguments->count != 1) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _clear\n");
|
interpreter->errorOutput("Incorrect number of arguments to clear\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1417,12 +1533,16 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_Literal obj = arguments->literals[0];
|
Toy_Literal obj = arguments->literals[0];
|
||||||
|
|
||||||
if (!TOY_IS_IDENTIFIER(idn)) {
|
if (!TOY_IS_IDENTIFIER(idn)) {
|
||||||
interpreter->errorOutput("expected identifier in _clear\n");
|
interpreter->errorOutput("expected identifier in clear\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_parseIdentifierToValue(interpreter, &obj);
|
Toy_parseIdentifierToValue(interpreter, &obj);
|
||||||
|
|
||||||
|
if (TOY_IS_IDENTIFIER(obj)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//NOTE: just pass in new compounds
|
//NOTE: just pass in new compounds
|
||||||
|
|
||||||
switch(obj.type) {
|
switch(obj.type) {
|
||||||
@@ -1433,7 +1553,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_Literal obj = TOY_TO_ARRAY_LITERAL(array);
|
Toy_Literal obj = TOY_TO_ARRAY_LITERAL(array);
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
||||||
interpreter->errorOutput("Incorrect type assigned to array in _clear: ");
|
interpreter->errorOutput("Incorrect type assigned to array in clear: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1451,7 +1571,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
Toy_Literal obj = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
Toy_Literal obj = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
|
||||||
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
|
||||||
interpreter->errorOutput("Incorrect type assigned to dictionary in _clear: ");
|
interpreter->errorOutput("Incorrect type assigned to dictionary in clear: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1463,7 +1583,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interpreter->errorOutput("Incorrect compound type in _clear: ");
|
interpreter->errorOutput("Incorrect compound type in clear: ");
|
||||||
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
Toy_printLiteralCustom(obj, interpreter->errorOutput);
|
||||||
interpreter->errorOutput("\n");
|
interpreter->errorOutput("\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
+42
-26
@@ -4,7 +4,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
//test variable sizes based on platform
|
//test variable sizes based on platform - see issue #35
|
||||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(char) == 1);
|
STATIC_ASSERT(sizeof(char) == 1);
|
||||||
@@ -15,23 +15,29 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
|
|||||||
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
||||||
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
||||||
|
|
||||||
//declare the singleton
|
static const char* build = __DATE__ " " __TIME__;
|
||||||
Toy_CommandLine Toy_commandLine;
|
|
||||||
|
const char* Toy_private_version_build() {
|
||||||
|
return build;
|
||||||
|
}
|
||||||
|
|
||||||
|
//declare the singleton with default values
|
||||||
|
Toy_CommandLine Toy_commandLine = {
|
||||||
|
.error = false,
|
||||||
|
.help = false,
|
||||||
|
.version = false,
|
||||||
|
.binaryfile = NULL,
|
||||||
|
.sourcefile = NULL,
|
||||||
|
.compilefile = NULL,
|
||||||
|
.outfile = "out.tb",
|
||||||
|
.source = NULL,
|
||||||
|
.initialfile = NULL,
|
||||||
|
.enablePrintNewline = true,
|
||||||
|
.parseBytecodeHeader = false,
|
||||||
|
.verbose = false
|
||||||
|
};
|
||||||
|
|
||||||
void Toy_initCommandLine(int argc, const char* argv[]) {
|
void Toy_initCommandLine(int argc, const char* argv[]) {
|
||||||
//default values
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
Toy_commandLine.help = false;
|
|
||||||
Toy_commandLine.version = false;
|
|
||||||
Toy_commandLine.binaryfile = NULL;
|
|
||||||
Toy_commandLine.sourcefile = NULL;
|
|
||||||
Toy_commandLine.compilefile = NULL;
|
|
||||||
Toy_commandLine.outfile = "out.tb";
|
|
||||||
Toy_commandLine.source = NULL;
|
|
||||||
Toy_commandLine.initialfile = NULL;
|
|
||||||
Toy_commandLine.enablePrintNewline = true;
|
|
||||||
Toy_commandLine.verbose = false;
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||||
Toy_commandLine.error = true; //error state by default, set to false by successful flags
|
Toy_commandLine.error = true; //error state by default, set to false by successful flags
|
||||||
|
|
||||||
@@ -88,6 +94,16 @@ void Toy_initCommandLine(int argc, const char* argv[]) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[i], "-p")) {
|
||||||
|
Toy_commandLine.parseBytecodeHeader = true;
|
||||||
|
|
||||||
|
if (Toy_commandLine.binaryfile) {
|
||||||
|
Toy_commandLine.error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-n")) {
|
if (!strcmp(argv[i], "-n")) {
|
||||||
Toy_commandLine.enablePrintNewline = false;
|
Toy_commandLine.enablePrintNewline = false;
|
||||||
Toy_commandLine.error = false;
|
Toy_commandLine.error = false;
|
||||||
@@ -115,16 +131,16 @@ void Toy_usageCommandLine(int argc, const char* argv[]) {
|
|||||||
void Toy_helpCommandLine(int argc, const char* argv[]) {
|
void Toy_helpCommandLine(int argc, const char* argv[]) {
|
||||||
Toy_usageCommandLine(argc, argv);
|
Toy_usageCommandLine(argc, argv);
|
||||||
|
|
||||||
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
printf(" -h, --help\t\t\tShow this help then exit.\n");
|
||||||
printf("-h\t| --help\t\tShow this help then exit.\n\n");
|
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
|
||||||
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
|
printf(" -d, --debug\t\t\tBe verbose when operating.\n");
|
||||||
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
|
printf(" -f, --file filename\t\tParse, compile and execute the source file.\n");
|
||||||
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
|
printf(" -i, --input source\t\tParse, compile and execute this given string of source code.\n");
|
||||||
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
|
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
|
||||||
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
|
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
|
||||||
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
|
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
|
||||||
printf("-t\t| --initial filename\tStart the repl as normal, after first running the given file.\n\n");
|
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
|
||||||
printf("-n\t|\t\t\tDisable the newline character at the end of the print statement.\n\n");
|
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
|
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
|
||||||
|
|||||||
+63
-6
@@ -1,15 +1,23 @@
|
|||||||
#pragma once
|
#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 <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define TOY_VERSION_MAJOR 0
|
/*!
|
||||||
#define TOY_VERSION_MINOR 9
|
### TOY_API
|
||||||
#define TOY_VERSION_PATCH 0
|
|
||||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
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__)
|
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||||
|
|
||||||
#define TOY_API extern
|
#define TOY_API extern
|
||||||
@@ -28,7 +36,54 @@
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//for processing the command line arguments
|
/*!
|
||||||
|
### 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 it’s 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 2
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### 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 {
|
typedef struct {
|
||||||
bool error;
|
bool error;
|
||||||
bool help;
|
bool help;
|
||||||
@@ -40,9 +95,11 @@ typedef struct {
|
|||||||
char* source;
|
char* source;
|
||||||
char* initialfile;
|
char* initialfile;
|
||||||
bool enablePrintNewline;
|
bool enablePrintNewline;
|
||||||
|
bool parseBytecodeHeader;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
} Toy_CommandLine;
|
} Toy_CommandLine;
|
||||||
|
|
||||||
|
//these are intended for the repl only, despite using the api prefix
|
||||||
TOY_API Toy_CommandLine Toy_commandLine;
|
TOY_API Toy_CommandLine Toy_commandLine;
|
||||||
|
|
||||||
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
|
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
|
||||||
|
|||||||
+201
-30
@@ -9,6 +9,7 @@
|
|||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
void Toy_initCompiler(Toy_Compiler* compiler) {
|
void Toy_initCompiler(Toy_Compiler* compiler) {
|
||||||
Toy_initLiteralArray(&compiler->literalCache);
|
Toy_initLiteralArray(&compiler->literalCache);
|
||||||
@@ -47,7 +48,7 @@ static int writeLiteralTypeToCache(Toy_LiteralArray* literalCache, Toy_Literal l
|
|||||||
}
|
}
|
||||||
|
|
||||||
//optimisation: check if exactly this literal array exists
|
//optimisation: check if exactly this literal array exists
|
||||||
int index = Toy_findLiteralIndex(literalCache, literal);
|
int index = Toy_private_findLiteralIndex(literalCache, literal);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
index = Toy_pushLiteralArray(literalCache, literal);
|
index = Toy_pushLiteralArray(literalCache, literal);
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
|||||||
switch(node->compound.nodes[i].pair.left->type) {
|
switch(node->compound.nodes[i].pair.left->type) {
|
||||||
case TOY_AST_NODE_LITERAL: {
|
case TOY_AST_NODE_LITERAL: {
|
||||||
//keys are literals
|
//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) {
|
if (key < 0) {
|
||||||
key = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
|
key = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
|
||||||
}
|
}
|
||||||
@@ -102,7 +103,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
|||||||
switch(node->compound.nodes[i].pair.right->type) {
|
switch(node->compound.nodes[i].pair.right->type) {
|
||||||
case TOY_AST_NODE_LITERAL: {
|
case TOY_AST_NODE_LITERAL: {
|
||||||
//values are literals
|
//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) {
|
if (val < 0) {
|
||||||
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
|
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//push the store to the cache, with instructions about how pack it
|
//push the store to the cache, with instructions about how pack it
|
||||||
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(store);
|
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL((Toy_LiteralDictionary*)store); //cast from array to dict, because it's intermediate
|
||||||
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it - nested in a dictionary
|
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it - nested in a dictionary
|
||||||
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
|
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
|
||||||
Toy_freeLiteral(literal);
|
Toy_freeLiteral(literal);
|
||||||
@@ -141,7 +142,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
|||||||
switch(node->compound.nodes[i].type) {
|
switch(node->compound.nodes[i].type) {
|
||||||
case TOY_AST_NODE_LITERAL: {
|
case TOY_AST_NODE_LITERAL: {
|
||||||
//values
|
//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) {
|
if (val < 0) {
|
||||||
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
|
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
|
||||||
}
|
}
|
||||||
@@ -229,7 +230,7 @@ static int writeNodeCollectionToCache(Toy_Compiler* compiler, Toy_ASTNode* node)
|
|||||||
|
|
||||||
static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
|
static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
|
||||||
//get the index
|
//get the index
|
||||||
int index = Toy_findLiteralIndex(&compiler->literalCache, literal);
|
int index = Toy_private_findLiteralIndex(&compiler->literalCache, literal);
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
if (TOY_IS_TYPE(literal)) {
|
if (TOY_IS_TYPE(literal)) {
|
||||||
@@ -258,6 +259,87 @@ static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BUGFIX: check to see if this node lies within this tree
|
||||||
|
bool checkNodeInTree(Toy_ASTNode* tree, Toy_ASTNode* node) {
|
||||||
|
if (tree == node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(tree->type) {
|
||||||
|
case TOY_AST_NODE_UNARY:
|
||||||
|
return checkNodeInTree(tree->unary.child, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_BINARY:
|
||||||
|
return checkNodeInTree(tree->binary.left, node) || checkNodeInTree(tree->binary.right, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_TERNARY:
|
||||||
|
return checkNodeInTree(tree->ternary.condition, node) || checkNodeInTree(tree->ternary.thenPath, node) || checkNodeInTree(tree->ternary.elsePath, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_GROUPING:
|
||||||
|
return checkNodeInTree(tree->grouping.child, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_BLOCK:
|
||||||
|
return checkNodeInTree(tree->block.nodes, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_COMPOUND:
|
||||||
|
return checkNodeInTree(tree->compound.nodes, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_PAIR:
|
||||||
|
return checkNodeInTree(tree->pair.left, node) || checkNodeInTree(tree->pair.right, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_INDEX:
|
||||||
|
return checkNodeInTree(tree->index.first, node) || checkNodeInTree(tree->index.second, node) || checkNodeInTree(tree->index.third, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_VAR_DECL:
|
||||||
|
return checkNodeInTree(tree->varDecl.expression, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_FN_COLLECTION:
|
||||||
|
return checkNodeInTree(tree->fnCollection.nodes, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_FN_DECL:
|
||||||
|
return checkNodeInTree(tree->fnDecl.arguments, node) || checkNodeInTree(tree->fnDecl.returns, node) || checkNodeInTree(tree->fnDecl.block, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_FN_CALL:
|
||||||
|
return checkNodeInTree(tree->fnCall.arguments, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_FN_RETURN:
|
||||||
|
return checkNodeInTree(tree->returns.returns, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_IF:
|
||||||
|
return checkNodeInTree(tree->pathIf.condition, node) || checkNodeInTree(tree->pathIf.thenPath, node) || checkNodeInTree(tree->pathIf.elsePath, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_WHILE:
|
||||||
|
return checkNodeInTree(tree->pathWhile.condition, node) || checkNodeInTree(tree->pathWhile.thenPath, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_FOR:
|
||||||
|
return checkNodeInTree(tree->pathFor.preClause, node) || checkNodeInTree(tree->pathFor.condition, node) || checkNodeInTree(tree->pathFor.postClause, node) || checkNodeInTree(tree->pathFor.thenPath, node);
|
||||||
|
|
||||||
|
case TOY_AST_NODE_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:
|
||||||
|
case TOY_AST_NODE_CONTINUE:
|
||||||
|
case TOY_AST_NODE_PREFIX_INCREMENT:
|
||||||
|
case TOY_AST_NODE_PREFIX_DECREMENT:
|
||||||
|
case TOY_AST_NODE_POSTFIX_INCREMENT:
|
||||||
|
case TOY_AST_NODE_POSTFIX_DECREMENT:
|
||||||
|
case TOY_AST_NODE_IMPORT:
|
||||||
|
case TOY_AST_NODE_PASS:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//NOTE: jumpOfsets are included, because function arg and return indexes are embedded in the code body i.e. need to include their sizes in the jump
|
//NOTE: jumpOfsets are included, because function arg and return indexes are embedded in the code body i.e. need to include their sizes in the jump
|
||||||
//NOTE: rootNode should NOT include groupings and blocks
|
//NOTE: rootNode should NOT include groupings and blocks
|
||||||
static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode* node, void* breakAddressesPtr, void* continueAddressesPtr, int jumpOffsets, Toy_ASTNode* rootNode) {
|
static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode* node, void* breakAddressesPtr, void* continueAddressesPtr, int jumpOffsets, Toy_ASTNode* rootNode) {
|
||||||
@@ -321,7 +403,8 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
//return this if...
|
//return this if...
|
||||||
Toy_Opcode ret = Toy_writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
Toy_Opcode ret = Toy_writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
|
||||||
|
|
||||||
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN) && rootNode->binary.right != node) { //range-based check for assignment type; make sure the index is on the left of the assignment symbol
|
//range-based check for assignment type; make sure the index is on the left of the assignment symbol
|
||||||
|
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN) && !checkNodeInTree(rootNode->binary.right, node)) {
|
||||||
return TOY_OP_INDEX_ASSIGN_INTERMEDIATE;
|
return TOY_OP_INDEX_ASSIGN_INTERMEDIATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,9 +413,35 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
return node->binary.opcode;
|
return node->binary.opcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != TOY_OP_EOF && (node->binary.opcode == TOY_OP_VAR_ASSIGN || node->binary.opcode == TOY_OP_AND || node->binary.opcode == TOY_OP_OR || (node->binary.opcode >= TOY_OP_COMPARE_EQUAL && node->binary.opcode <= TOY_OP_INVERT))) {
|
//untangle in these cases - (WTF, are you serious?)
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
|
if (ret != TOY_OP_EOF) {
|
||||||
ret = TOY_OP_EOF; //untangle in this case
|
switch(node->binary.opcode) {
|
||||||
|
case TOY_OP_NEGATE:
|
||||||
|
case TOY_OP_ADDITION:
|
||||||
|
case TOY_OP_SUBTRACTION:
|
||||||
|
case TOY_OP_MULTIPLICATION:
|
||||||
|
case TOY_OP_DIVISION:
|
||||||
|
case TOY_OP_MODULO:
|
||||||
|
case TOY_OP_VAR_ASSIGN:
|
||||||
|
case TOY_OP_VAR_ADDITION_ASSIGN:
|
||||||
|
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
|
||||||
|
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
|
||||||
|
case TOY_OP_VAR_DIVISION_ASSIGN:
|
||||||
|
case TOY_OP_VAR_MODULO_ASSIGN:
|
||||||
|
case TOY_OP_COMPARE_EQUAL:
|
||||||
|
case TOY_OP_COMPARE_NOT_EQUAL:
|
||||||
|
case TOY_OP_COMPARE_LESS:
|
||||||
|
case TOY_OP_COMPARE_LESS_EQUAL:
|
||||||
|
case TOY_OP_COMPARE_GREATER:
|
||||||
|
case TOY_OP_COMPARE_GREATER_EQUAL:
|
||||||
|
case TOY_OP_INVERT:
|
||||||
|
//place the rhs result before the outer instruction
|
||||||
|
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
|
||||||
|
ret = TOY_OP_EOF;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
|
||||||
@@ -445,7 +554,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
//write each piece of the declaration to the bytecode
|
//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) {
|
if (identifierIndex < 0) {
|
||||||
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->varDecl.identifier);
|
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->varDecl.identifier);
|
||||||
}
|
}
|
||||||
@@ -478,9 +587,13 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
Toy_initCompiler(fnCompiler);
|
Toy_initCompiler(fnCompiler);
|
||||||
Toy_writeCompiler(fnCompiler, node->fnDecl.arguments); //can be empty, but not NULL
|
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_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
|
//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)
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
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
|
//adopt the panic state if anything happened
|
||||||
@@ -489,11 +602,10 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
//create the function in the literal cache (by storing the compiler object)
|
//create the function in the literal cache (by storing the compiler object)
|
||||||
Toy_Literal fnLiteral = TOY_TO_FUNCTION_LITERAL(fnCompiler, 0);
|
Toy_Literal fnLiteral = ((Toy_Literal){ .as = { .generic = fnCompiler }, .type = TOY_LITERAL_FUNCTION_INTERMEDIATE});
|
||||||
fnLiteral.type = TOY_LITERAL_FUNCTION_INTERMEDIATE; //NOTE: changing type
|
|
||||||
|
|
||||||
//push the name
|
//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) {
|
if (identifierIndex < 0) {
|
||||||
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnDecl.identifier);
|
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnDecl.identifier);
|
||||||
}
|
}
|
||||||
@@ -541,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
|
for (int i = 0; i < node->fnCall.arguments->fnCollection.count; i++) { //reverse order, to count from the beginning in the interpreter
|
||||||
//sub-calls
|
//sub-calls
|
||||||
if (node->fnCall.arguments->fnCollection.nodes[i].type != TOY_AST_NODE_LITERAL) {
|
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
|
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||||
}
|
}
|
||||||
@@ -549,7 +661,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
//write each argument to the bytecode
|
//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) {
|
if (argumentsIndex < 0) {
|
||||||
argumentsIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
|
argumentsIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
|
||||||
}
|
}
|
||||||
@@ -571,7 +683,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
|
|
||||||
//push the argument COUNT to the top of the stack
|
//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
|
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) {
|
if (argumentsCountIndex < 0) {
|
||||||
argumentsCountIndex = Toy_pushLiteralArray(&compiler->literalCache, argumentsCountLiteral);
|
argumentsCountIndex = Toy_pushLiteralArray(&compiler->literalCache, argumentsCountLiteral);
|
||||||
}
|
}
|
||||||
@@ -729,12 +841,20 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
compiler->count += sizeof(unsigned short); //2 bytes
|
compiler->count += sizeof(unsigned short); //2 bytes
|
||||||
|
|
||||||
//write the body
|
//write the body
|
||||||
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_BEGIN; //1 byte
|
bool closeScope = false;
|
||||||
|
if (node->pathFor.thenPath->type != TOY_AST_NODE_BLOCK) {
|
||||||
|
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_BEGIN; //1 byte
|
||||||
|
closeScope = true;
|
||||||
|
}
|
||||||
|
|
||||||
override = Toy_writeCompilerWithJumps(compiler, node->pathFor.thenPath, &breakAddresses, &continueAddresses, jumpOffsets, rootNode);
|
override = Toy_writeCompilerWithJumps(compiler, node->pathFor.thenPath, &breakAddresses, &continueAddresses, jumpOffsets, rootNode);
|
||||||
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
|
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||||
}
|
}
|
||||||
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_END; //1 byte
|
|
||||||
|
if (closeScope) {
|
||||||
|
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_END; //1 byte
|
||||||
|
}
|
||||||
|
|
||||||
//for-breaks actually jump to the bottom
|
//for-breaks actually jump to the bottom
|
||||||
int jumpToIncrement = compiler->count;
|
int jumpToIncrement = compiler->count;
|
||||||
@@ -745,6 +865,9 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BUGFIX: clear the stack after each loop
|
||||||
|
compiler->bytecode[compiler->count++] = TOY_OP_POP_STACK; //1 byte
|
||||||
|
|
||||||
compiler->bytecode[compiler->count++] = TOY_OP_JUMP; //1 byte
|
compiler->bytecode[compiler->count++] = TOY_OP_JUMP; //1 byte
|
||||||
unsigned short tmpVal = jumpToStart + jumpOffsets;
|
unsigned short tmpVal = jumpToStart + jumpOffsets;
|
||||||
memcpy(compiler->bytecode + compiler->count, &tmpVal, sizeof(tmpVal));
|
memcpy(compiler->bytecode + compiler->count, &tmpVal, sizeof(tmpVal));
|
||||||
@@ -768,9 +891,6 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
memcpy(compiler->bytecode + point, &tmpVal, sizeof(tmpVal));
|
memcpy(compiler->bytecode + point, &tmpVal, sizeof(tmpVal));
|
||||||
}
|
}
|
||||||
|
|
||||||
//clear the stack after use
|
|
||||||
compiler->bytecode[compiler->count++] = TOY_OP_POP_STACK; //1 byte
|
|
||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
Toy_freeLiteralArray(&breakAddresses);
|
Toy_freeLiteralArray(&breakAddresses);
|
||||||
Toy_freeLiteralArray(&continueAddresses);
|
Toy_freeLiteralArray(&continueAddresses);
|
||||||
@@ -813,6 +933,54 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
|
|||||||
}
|
}
|
||||||
break;
|
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: {
|
case TOY_AST_NODE_FN_RETURN: {
|
||||||
//read each returned literal onto the stack, and return the number of values to 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++) {
|
for (int i = 0; i < node->returns.returns->fnCollection.count; i++) {
|
||||||
@@ -981,6 +1149,8 @@ void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node) {
|
|||||||
if (op != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
|
if (op != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
|
||||||
compiler->bytecode[compiler->count++] = (unsigned char)op; //1 byte
|
compiler->bytecode[compiler->count++] = (unsigned char)op; //1 byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: could free up AST Nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_freeCompiler(Toy_Compiler* compiler) {
|
void Toy_freeCompiler(Toy_Compiler* compiler) {
|
||||||
@@ -989,6 +1159,7 @@ void Toy_freeCompiler(Toy_Compiler* compiler) {
|
|||||||
compiler->bytecode = NULL;
|
compiler->bytecode = NULL;
|
||||||
compiler->capacity = 0;
|
compiler->capacity = 0;
|
||||||
compiler->count = 0;
|
compiler->count = 0;
|
||||||
|
compiler->panic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emitByte(unsigned char** collationPtr, int* capacityPtr, int* countPtr, unsigned char byte) {
|
static void emitByte(unsigned char** collationPtr, int* capacityPtr, int* countPtr, unsigned char byte) {
|
||||||
@@ -1036,7 +1207,7 @@ static void emitFloat(unsigned char** collationPtr, int* capacityPtr, int* count
|
|||||||
}
|
}
|
||||||
|
|
||||||
//return the result
|
//return the result
|
||||||
static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size, bool embedHeader) {
|
static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, size_t* size, bool embedHeader) {
|
||||||
if (compiler->panic) {
|
if (compiler->panic) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't collate a panicked compiler\n" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "[internal] Can't collate a panicked compiler\n" TOY_CC_RESET);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1178,17 +1349,17 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
|||||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE: {
|
case TOY_LITERAL_FUNCTION_INTERMEDIATE: {
|
||||||
//extract the compiler
|
//extract the compiler
|
||||||
Toy_Literal fn = compiler->literalCache.literals[i];
|
Toy_Literal fn = compiler->literalCache.literals[i];
|
||||||
void* fnCompiler = TOY_AS_FUNCTION(fn).inner.bytecode; //store the compiler here for now
|
void* fnCompiler = fn.as.generic; //store the compiler here for now
|
||||||
|
|
||||||
//collate the function into bytecode (without header)
|
//collate the function into bytecode (without header)
|
||||||
int size = 0;
|
size_t size = 0;
|
||||||
unsigned char* bytes = collateCompilerHeaderOpt((Toy_Compiler*)fnCompiler, &size, false);
|
unsigned char* bytes = collateCompilerHeaderOpt((Toy_Compiler*)fnCompiler, &size, false);
|
||||||
|
|
||||||
//emit how long this section is, +1 for ending mark
|
//emit how long this section is, +1 for ending mark
|
||||||
Toy_emitShort(&fnCollation, &fnCapacity, &fnCount, (unsigned short)size + 1);
|
Toy_emitShort(&fnCollation, &fnCapacity, &fnCount, (unsigned short)size + 1);
|
||||||
|
|
||||||
//write the fn to the fn collation
|
//write the fn to the fn collation
|
||||||
for (int i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
emitByte(&fnCollation, &fnCapacity, &fnCount, bytes[i]);
|
emitByte(&fnCollation, &fnCapacity, &fnCount, bytes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1296,6 +1467,6 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
|
|||||||
}
|
}
|
||||||
|
|
||||||
//the whole point of the compiler is to alter bytecode, so leave it as non-const
|
//the whole point of the compiler is to alter bytecode, so leave it as non-const
|
||||||
unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, int* size) {
|
unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size) {
|
||||||
return collateCompilerHeaderOpt(compiler, size, true);
|
return collateCompilerHeaderOpt(compiler, size, true);
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-6
@@ -1,11 +1,20 @@
|
|||||||
#pragma once
|
#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 program’s 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_common.h"
|
||||||
#include "toy_opcodes.h"
|
#include "toy_opcodes.h"
|
||||||
#include "toy_ast_node.h"
|
#include "toy_ast_node.h"
|
||||||
#include "toy_literal_array.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 {
|
typedef struct Toy_Compiler {
|
||||||
Toy_LiteralArray literalCache;
|
Toy_LiteralArray literalCache;
|
||||||
unsigned char* bytecode;
|
unsigned char* bytecode;
|
||||||
@@ -14,9 +23,38 @@ typedef struct Toy_Compiler {
|
|||||||
bool panic;
|
bool panic;
|
||||||
} Toy_Compiler;
|
} Toy_Compiler;
|
||||||
|
|
||||||
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
|
/*!
|
||||||
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
|
## Define Functions
|
||||||
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
|
|
||||||
|
|
||||||
//embed the header, data section, code section, function section, etc.
|
Executing the following functions out-of-order causes undefiend behaviour.
|
||||||
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, int* size);
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### 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);
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
//NOTE: you need both font AND background for these to work
|
/* toy_console_colors.h - console utility
|
||||||
|
|
||||||
|
This file provides a number of macros that can set the color of text in a console
|
||||||
|
window. These are used for convenience only. They are supposed to be dropped into
|
||||||
|
a printf()'s first argument, like so:
|
||||||
|
|
||||||
|
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
|
||||||
|
|
||||||
|
NOTE: you need both font AND background for these to work
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
//platform/compiler-specific instructions
|
//platform/compiler-specific instructions
|
||||||
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||||
|
|||||||
+703
-650
File diff suppressed because it is too large
Load Diff
+160
-13
@@ -1,13 +1,47 @@
|
|||||||
#pragma once
|
#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_common.h"
|
||||||
#include "toy_literal.h"
|
#include "toy_literal.h"
|
||||||
#include "toy_literal_array.h"
|
#include "toy_literal_array.h"
|
||||||
#include "toy_literal_dictionary.h"
|
#include "toy_literal_dictionary.h"
|
||||||
#include "toy_scope.h"
|
#include "toy_scope.h"
|
||||||
|
|
||||||
typedef void (*Toy_PrintFn)(const char*);
|
|
||||||
|
|
||||||
//the interpreter acts depending on the bytecode instructions
|
//the interpreter acts depending on the bytecode instructions
|
||||||
typedef struct Toy_Interpreter {
|
typedef struct Toy_Interpreter {
|
||||||
//input
|
//input
|
||||||
@@ -33,21 +67,134 @@ typedef struct Toy_Interpreter {
|
|||||||
bool panic;
|
bool panic;
|
||||||
} Toy_Interpreter;
|
} 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);
|
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);
|
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);
|
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);
|
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);
|
### 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);
|
|
||||||
|
|
||||||
//main access
|
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:
|
||||||
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
|
```c
|
||||||
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
Toy_Literal foobarIdn = foobar;
|
||||||
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
|
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);
|
||||||
|
|||||||
+11
-11
@@ -117,7 +117,7 @@ static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
|
|||||||
#ifndef TOY_EXPORT
|
#ifndef TOY_EXPORT
|
||||||
if (Toy_commandLine.verbose) {
|
if (Toy_commandLine.verbose) {
|
||||||
printf("err:");
|
printf("err:");
|
||||||
Toy_printToken(&token);
|
Toy_private_printToken(&token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
|||||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||||
if (Toy_commandLine.verbose) {
|
if (Toy_commandLine.verbose) {
|
||||||
printf("tok:");
|
printf("tok:");
|
||||||
Toy_printToken(&token);
|
Toy_private_printToken(&token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
|||||||
} else {
|
} else {
|
||||||
printf("flt:");
|
printf("flt:");
|
||||||
}
|
}
|
||||||
Toy_printToken(&token);
|
Toy_private_printToken(&token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
|||||||
#ifndef TOY_EXPORT
|
#ifndef TOY_EXPORT
|
||||||
if (Toy_commandLine.verbose) {
|
if (Toy_commandLine.verbose) {
|
||||||
printf("str:");
|
printf("str:");
|
||||||
Toy_printToken(&token);
|
Toy_private_printToken(&token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
|||||||
|
|
||||||
//scan for a keyword
|
//scan for a keyword
|
||||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
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;
|
Toy_Token token;
|
||||||
|
|
||||||
token.type = Toy_keywordTypes[i].type;
|
token.type = Toy_keywordTypes[i].type;
|
||||||
@@ -248,7 +248,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
|||||||
#ifndef TOY_EXPORT
|
#ifndef TOY_EXPORT
|
||||||
if (Toy_commandLine.verbose) {
|
if (Toy_commandLine.verbose) {
|
||||||
printf("kwd:");
|
printf("kwd:");
|
||||||
Toy_printToken(&token);
|
Toy_private_printToken(&token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
|||||||
#ifndef TOY_EXPORT
|
#ifndef TOY_EXPORT
|
||||||
if (Toy_commandLine.verbose) {
|
if (Toy_commandLine.verbose) {
|
||||||
printf("idf:");
|
printf("idf:");
|
||||||
Toy_printToken(&token);
|
Toy_private_printToken(&token);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
|
|||||||
lexer->source = source;
|
lexer->source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
|
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
||||||
eatWhitespace(lexer);
|
eatWhitespace(lexer);
|
||||||
|
|
||||||
lexer->start = lexer->current;
|
lexer->start = lexer->current;
|
||||||
@@ -317,10 +317,10 @@ Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
|
|||||||
if (advance(lexer) != '&') {
|
if (advance(lexer) != '&') {
|
||||||
return makeErrorToken(lexer, "Unexpected '&'");
|
return makeErrorToken(lexer, "Unexpected '&'");
|
||||||
} else {
|
} 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_QUESTION);
|
||||||
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
|
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
|
||||||
@@ -352,7 +352,7 @@ static void trim(char** s, int* l) { //all this to remove a newline?
|
|||||||
}
|
}
|
||||||
|
|
||||||
//for debugging
|
//for debugging
|
||||||
void Toy_printToken(Toy_Token* token) {
|
void Toy_private_printToken(Toy_Token* token) {
|
||||||
if (token->type == TOY_TOKEN_ERROR) {
|
if (token->type == TOY_TOKEN_ERROR) {
|
||||||
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
|
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
|
||||||
return;
|
return;
|
||||||
|
|||||||
+39
-3
@@ -1,5 +1,11 @@
|
|||||||
#pragma once
|
#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_common.h"
|
||||||
#include "toy_token_types.h"
|
#include "toy_token_types.h"
|
||||||
|
|
||||||
@@ -20,10 +26,40 @@ typedef struct {
|
|||||||
int line;
|
int line;
|
||||||
} Toy_Token;
|
} 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_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
|
||||||
TOY_API Toy_Token Toy_scanLexer(Toy_Lexer* lexer);
|
|
||||||
|
|
||||||
//for debugging
|
/*!
|
||||||
TOY_API void Toy_printToken(Toy_Token* token);
|
### 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);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### 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);
|
TOY_API void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
|
||||||
|
|||||||
+35
-24
@@ -8,6 +8,7 @@
|
|||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
//hash util functions
|
//hash util functions
|
||||||
static unsigned int hashString(const char* string, int length) {
|
static unsigned int hashString(const char* string, int length) {
|
||||||
@@ -58,10 +59,10 @@ void Toy_freeLiteral(Toy_Literal literal) {
|
|||||||
if (TOY_IS_FUNCTION(literal)) {
|
if (TOY_IS_FUNCTION(literal)) {
|
||||||
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
|
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
|
||||||
TOY_AS_FUNCTION(literal).scope = NULL;
|
TOY_AS_FUNCTION(literal).scope = NULL;
|
||||||
TOY_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(literal));
|
Toy_deleteRefFunction((Toy_RefFunction*)(TOY_AS_FUNCTION(literal).inner.ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TOY_IS_TYPE(literal)) {
|
if (TOY_IS_TYPE(literal) && TOY_AS_TYPE(literal).capacity > 0) {
|
||||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||||
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
|
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
|
||||||
}
|
}
|
||||||
@@ -83,12 +84,8 @@ bool Toy_private_isTruthy(Toy_Literal x) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr) {
|
|
||||||
return ((Toy_Literal){{ .string.ptr = ptr },TOY_LITERAL_STRING, 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
|
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
|
||||||
return ((Toy_Literal){{ .identifier.ptr = ptr, .identifier.hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) },TOY_LITERAL_IDENTIFIER, 0});
|
return ((Toy_Literal){{ .identifier = { .ptr = ptr, .hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }},TOY_LITERAL_IDENTIFIER});
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
|
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
|
||||||
@@ -122,6 +119,10 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
|||||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||||
Toy_initLiteralArray(array);
|
Toy_initLiteralArray(array);
|
||||||
|
|
||||||
|
//preallocate enough space
|
||||||
|
array->capacity = TOY_AS_ARRAY(original)->capacity;
|
||||||
|
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, 0, array->capacity);
|
||||||
|
|
||||||
//copy each element
|
//copy each element
|
||||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||||
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
|
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
|
||||||
@@ -134,6 +135,15 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
|||||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||||
Toy_initLiteralDictionary(dictionary);
|
Toy_initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//preallocate enough space
|
||||||
|
dictionary->capacity = TOY_AS_DICTIONARY(original)->capacity;
|
||||||
|
dictionary->entries = TOY_ALLOCATE(Toy_private_dictionary_entry, dictionary->capacity);
|
||||||
|
|
||||||
|
for (int i = 0; i < dictionary->capacity; i++) {
|
||||||
|
dictionary->entries[i].key = TOY_TO_NULL_LITERAL;
|
||||||
|
dictionary->entries[i].value = TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
//copy each entry
|
//copy each entry
|
||||||
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
|
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
|
||||||
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
|
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
|
||||||
@@ -145,17 +155,16 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_FUNCTION: {
|
case TOY_LITERAL_FUNCTION: {
|
||||||
unsigned char* buffer = TOY_ALLOCATE(unsigned char, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
|
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(Toy_copyRefFunction( TOY_AS_FUNCTION(original).inner.ptr ));
|
||||||
memcpy(buffer, TOY_AS_FUNCTION(original).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
|
|
||||||
|
|
||||||
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(buffer, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
|
|
||||||
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
|
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
|
||||||
|
|
||||||
return literal;
|
return literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_IDENTIFIER: {
|
case TOY_LITERAL_IDENTIFIER: {
|
||||||
return TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_IDENTIFIER(original)));
|
//NOTE: could optimise this by copying the hash manually, but it's a very small increase in performance
|
||||||
|
return TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_IDENTIFIER(original)));
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_TYPE: {
|
case TOY_LITERAL_TYPE: {
|
||||||
@@ -172,7 +181,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
|||||||
return original; //literally a shallow copy
|
return original; //literally a shallow copy
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_ARRAY_INTERMEDIATE: {
|
case TOY_LITERAL_ARRAY_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||||
Toy_initLiteralArray(array);
|
Toy_initLiteralArray(array);
|
||||||
|
|
||||||
@@ -188,7 +197,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
|
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||||
Toy_initLiteralArray(array);
|
Toy_initLiteralArray(array);
|
||||||
|
|
||||||
@@ -204,7 +213,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_TYPE_INTERMEDIATE: {
|
case TOY_LITERAL_TYPE_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||||
Toy_initLiteralArray(array);
|
Toy_initLiteralArray(array);
|
||||||
|
|
||||||
@@ -239,10 +248,10 @@ bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
|
|||||||
// ints and floats are compatible
|
// ints and floats are compatible
|
||||||
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
|
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
|
||||||
if (TOY_IS_INTEGER(lhs)) {
|
if (TOY_IS_INTEGER(lhs)) {
|
||||||
return TOY_AS_INTEGER(lhs) + TOY_AS_FLOAT(rhs);
|
return TOY_AS_INTEGER(lhs) == TOY_AS_FLOAT(rhs);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return TOY_AS_FLOAT(lhs) + TOY_AS_INTEGER(rhs);
|
return TOY_AS_FLOAT(lhs) == TOY_AS_INTEGER(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,8 +385,10 @@ int Toy_hashLiteral(Toy_Literal lit) {
|
|||||||
case TOY_LITERAL_INTEGER:
|
case TOY_LITERAL_INTEGER:
|
||||||
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
|
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
|
||||||
|
|
||||||
case TOY_LITERAL_FLOAT:
|
case TOY_LITERAL_FLOAT: {
|
||||||
return hashUInt(*(unsigned int*)(&TOY_AS_FLOAT(lit)));
|
unsigned int* ptr = (unsigned int*)(&TOY_AS_FLOAT(lit));
|
||||||
|
return hashUInt(*ptr);
|
||||||
|
}
|
||||||
|
|
||||||
case TOY_LITERAL_STRING:
|
case TOY_LITERAL_STRING:
|
||||||
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
|
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
|
||||||
@@ -404,20 +415,20 @@ int Toy_hashLiteral(Toy_Literal lit) {
|
|||||||
case TOY_LITERAL_FUNCTION:
|
case TOY_LITERAL_FUNCTION:
|
||||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||||
case TOY_LITERAL_FUNCTION_HOOK:
|
case TOY_LITERAL_FUNCTION_HOOK:
|
||||||
return 0; //can't hash these
|
return -1; //can't hash these
|
||||||
|
|
||||||
case TOY_LITERAL_IDENTIFIER:
|
case TOY_LITERAL_IDENTIFIER:
|
||||||
return TOY_HASH_I(lit); //pre-computed
|
return TOY_HASH_I(lit); //pre-computed
|
||||||
|
|
||||||
case TOY_LITERAL_TYPE:
|
case TOY_LITERAL_TYPE:
|
||||||
return TOY_AS_TYPE(lit).typeOf; //nothing else I can do
|
return -1; //not much i can really do
|
||||||
|
|
||||||
case TOY_LITERAL_OPAQUE:
|
case TOY_LITERAL_OPAQUE:
|
||||||
case TOY_LITERAL_ANY:
|
case TOY_LITERAL_ANY:
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//should never bee seen
|
//should never be seen
|
||||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
|
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -444,8 +455,8 @@ static void printToBuffer(const char* str) {
|
|||||||
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
|
size_t total = snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str ? str : "\0");
|
||||||
globalPrintCount += strlen(str);
|
globalPrintCount += total;
|
||||||
}
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
@@ -453,7 +464,7 @@ void Toy_printLiteral(Toy_Literal literal) {
|
|||||||
Toy_printLiteralCustom(literal, stdoutWrapper);
|
Toy_printLiteralCustom(literal, stdoutWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
|
void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn printFn) {
|
||||||
switch(literal.type) {
|
switch(literal.type) {
|
||||||
case TOY_LITERAL_NULL:
|
case TOY_LITERAL_NULL:
|
||||||
printFn("null");
|
printFn("null");
|
||||||
|
|||||||
+258
-33
@@ -1,17 +1,54 @@
|
|||||||
#pragma once
|
#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_common.h"
|
||||||
|
|
||||||
#include "toy_refstring.h"
|
#include "toy_refstring.h"
|
||||||
|
#include "toy_reffunction.h"
|
||||||
|
|
||||||
//forward delcare stuff
|
//forward delcare stuff
|
||||||
struct Toy_Literal;
|
struct Toy_Literal;
|
||||||
struct Toy_Interpreter;
|
struct Toy_Interpreter;
|
||||||
struct Toy_LiteralArray;
|
struct Toy_LiteralArray;
|
||||||
|
struct Toy_LiteralDictionary;
|
||||||
|
struct Toy_Scope;
|
||||||
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
|
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
|
||||||
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
|
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
|
||||||
|
typedef void (*Toy_PrintFn)(const char*);
|
||||||
|
|
||||||
#include <string.h>
|
/*!
|
||||||
|
## 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 {
|
typedef enum {
|
||||||
TOY_LITERAL_NULL,
|
TOY_LITERAL_NULL,
|
||||||
@@ -49,25 +86,25 @@ typedef struct Toy_Literal {
|
|||||||
//string hash?
|
//string hash?
|
||||||
} string; //8
|
} string; //8
|
||||||
|
|
||||||
void* array; //8
|
struct Toy_LiteralArray* array; //8
|
||||||
void* dictionary; //8
|
struct Toy_LiteralDictionary* dictionary; //8
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
union {
|
union {
|
||||||
void* bytecode; //8
|
Toy_RefFunction* ptr; //8
|
||||||
Toy_NativeFn native; //8
|
Toy_NativeFn native; //8
|
||||||
Toy_HookFn hook; //8
|
Toy_HookFn hook; //8
|
||||||
} inner; //8
|
} inner; //8
|
||||||
void* scope; //8
|
struct Toy_Scope* scope; //8
|
||||||
} function; //16
|
} function; //16
|
||||||
|
|
||||||
struct { //for variable names
|
struct { //for variable names
|
||||||
Toy_RefString* ptr; //8
|
Toy_RefString* ptr; //8
|
||||||
int hash; //4
|
int hash; //4
|
||||||
} identifier; //16
|
} identifier; //16
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void* subtypes; //8
|
struct Toy_Literal* subtypes; //8
|
||||||
Toy_LiteralType typeOf; //4
|
Toy_LiteralType typeOf; //4
|
||||||
unsigned char capacity; //1
|
unsigned char capacity; //1
|
||||||
unsigned char count; //1
|
unsigned char count; //1
|
||||||
@@ -78,12 +115,37 @@ typedef struct Toy_Literal {
|
|||||||
void* ptr; //8
|
void* ptr; //8
|
||||||
int tag; //4
|
int tag; //4
|
||||||
} opaque; //16
|
} opaque; //16
|
||||||
|
|
||||||
|
void* generic; //8
|
||||||
} as; //16
|
} as; //16
|
||||||
|
|
||||||
Toy_LiteralType type; //4
|
Toy_LiteralType type; //4
|
||||||
int bytecodeLength; //4 - shenanigans with byte alignment reduces the size of Toy_Literal
|
//4 - unused
|
||||||
|
//shenanigans with byte alignment reduces the size of Toy_Literal
|
||||||
} Toy_Literal;
|
} 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_NULL(value) ((value).type == TOY_LITERAL_NULL)
|
||||||
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
||||||
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
||||||
@@ -98,6 +160,23 @@ typedef struct Toy_Literal {
|
|||||||
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
|
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
|
||||||
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
|
#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_BOOLEAN(value) ((value).as.boolean)
|
||||||
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
||||||
#define TOY_AS_FLOAT(value) ((value).as.number)
|
#define TOY_AS_FLOAT(value) ((value).as.number)
|
||||||
@@ -111,45 +190,191 @@ typedef struct Toy_Literal {
|
|||||||
#define TOY_AS_TYPE(value) ((value).as.type)
|
#define TOY_AS_TYPE(value) ((value).as.type)
|
||||||
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
|
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||||
|
|
||||||
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL, 0})
|
/*!
|
||||||
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN, 0})
|
The following macros are used to create a new literal, with the given `value` as it's internal value.
|
||||||
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER, 0})
|
|
||||||
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT, 0})
|
* `TOY_TO_NULL_LITERAL` - does not need parantheses
|
||||||
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
|
* `TOY_TO_BOOLEAN_LITERAL(value)`
|
||||||
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY, 0})
|
* `TOY_TO_INTEGER_LITERAL(value)`
|
||||||
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY, 0})
|
* `TOY_TO_FLOAT_LITERAL(value)`
|
||||||
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){{ .function.inner.bytecode = value, .function.scope = NULL }, TOY_LITERAL_FUNCTION, l})
|
* `TOY_TO_STRING_LITERAL(value)`
|
||||||
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function.inner.native = value, .function.scope = NULL }, TOY_LITERAL_FUNCTION_NATIVE, 0})
|
* `TOY_TO_ARRAY_LITERAL(value)`
|
||||||
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function.inner.hook = value, .function.scope = NULL }, TOY_LITERAL_FUNCTION_HOOK, 0})
|
* `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})
|
||||||
|
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT})
|
||||||
|
#define TOY_TO_STRING_LITERAL(value) ((Toy_Literal){{ .string = { .ptr = value }},TOY_LITERAL_STRING})
|
||||||
|
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY})
|
||||||
|
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY})
|
||||||
|
#define TOY_TO_FUNCTION_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .ptr = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION})
|
||||||
|
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE})
|
||||||
|
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK})
|
||||||
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
||||||
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }, TOY_LITERAL_TYPE, 0})
|
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE})
|
||||||
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque.ptr = value, .opaque.tag = t }, TOY_LITERAL_OPAQUE, 0})
|
#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_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
||||||
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK, 0})
|
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
|
||||||
|
|
||||||
TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
/*!
|
||||||
|
## 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.
|
||||||
|
!*/
|
||||||
|
|
||||||
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) ((lit).bytecodeLength)
|
/*!
|
||||||
|
### 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
|
#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)
|
#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)
|
#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
|
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||||
|
|
||||||
//BUGFIX: macros are not functions
|
/*!
|
||||||
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
## Defined Functions
|
||||||
TOY_API Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr);
|
!*/
|
||||||
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
|
|
||||||
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
|
|
||||||
|
|
||||||
//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);
|
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);
|
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);
|
TOY_API int Toy_hashLiteral(Toy_Literal lit);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### 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);
|
TOY_API void Toy_printLiteral(Toy_Literal literal);
|
||||||
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*));
|
|
||||||
|
/*!
|
||||||
|
### 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);
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ void Toy_freeLiteralArray(Toy_LiteralArray* array) {
|
|||||||
Toy_freeLiteral(array->literals[i]);
|
Toy_freeLiteral(array->literals[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
|
if (array->capacity > 0) {
|
||||||
Toy_initLiteralArray(array);
|
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
|
||||||
|
Toy_initLiteralArray(array);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
|
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||||
@@ -50,7 +52,7 @@ Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//find a literal in the array that matches the "literal" argument
|
//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++) {
|
for (int i = 0; i < array->count; i++) {
|
||||||
//not the same type
|
//not the same type
|
||||||
if (array->literals[i].type != literal.type) {
|
if (array->literals[i].type != literal.type) {
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
#pragma once
|
#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_common.h"
|
||||||
|
|
||||||
#include "toy_literal.h"
|
#include "toy_literal.h"
|
||||||
@@ -10,11 +18,65 @@ typedef struct Toy_LiteralArray {
|
|||||||
int count;
|
int count;
|
||||||
} Toy_LiteralArray;
|
} 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);
|
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);
|
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_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);
|
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_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);
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
//util functions
|
//util functions
|
||||||
static void setEntryValues(Toy_private_entry* entry, Toy_Literal key, Toy_Literal value) {
|
static void setEntryValues(Toy_private_dictionary_entry* entry, Toy_Literal key, Toy_Literal value) {
|
||||||
//much simpler now
|
//much simpler now
|
||||||
Toy_freeLiteral(entry->key);
|
Toy_freeLiteral(entry->key);
|
||||||
entry->key = Toy_copyLiteral(key);
|
entry->key = Toy_copyLiteral(key);
|
||||||
@@ -16,17 +16,23 @@ static void setEntryValues(Toy_private_entry* entry, Toy_Literal key, Toy_Litera
|
|||||||
entry->value = Toy_copyLiteral(value);
|
entry->value = Toy_copyLiteral(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_private_entry* getEntryArray(Toy_private_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
|
static Toy_private_dictionary_entry* getEntryArray(Toy_private_dictionary_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
|
||||||
|
if (!capacity) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
//find "key", starting at index
|
//find "key", starting at index
|
||||||
unsigned int index = hash % capacity;
|
int index = hash % capacity;
|
||||||
unsigned int start = index;
|
int start = index;
|
||||||
|
|
||||||
//increment once, so it can't equal start
|
//increment once, so it can't equal start
|
||||||
index = (index + 1) % capacity;
|
if (++index >= capacity) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
//literal probing and collision checking
|
//literal probing and collision checking
|
||||||
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
||||||
Toy_private_entry* entry = &array[index];
|
Toy_private_dictionary_entry* entry = &array[index];
|
||||||
|
|
||||||
if (TOY_IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
if (TOY_IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
||||||
if (TOY_IS_NULL(entry->value) && !mustExist) {
|
if (TOY_IS_NULL(entry->value) && !mustExist) {
|
||||||
@@ -40,15 +46,18 @@ static Toy_private_entry* getEntryArray(Toy_private_entry* array, int capacity,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
index = (index + 1) % capacity;
|
if (++index >= capacity) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
//index = (index + 1) % capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adjustEntryCapacity(Toy_private_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
static void adjustEntryCapacity(Toy_private_dictionary_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
||||||
//new entry space
|
//new entry space
|
||||||
Toy_private_entry* newEntries = TOY_ALLOCATE(Toy_private_entry, capacity);
|
Toy_private_dictionary_entry* newEntries = TOY_ALLOCATE(Toy_private_dictionary_entry, capacity);
|
||||||
|
|
||||||
for (int i = 0; i < capacity; i++) {
|
for (int i = 0; i < capacity; i++) {
|
||||||
newEntries[i].key = TOY_TO_NULL_LITERAL;
|
newEntries[i].key = TOY_TO_NULL_LITERAL;
|
||||||
@@ -62,19 +71,21 @@ static void adjustEntryCapacity(Toy_private_entry** dictionaryHandle, int oldCap
|
|||||||
}
|
}
|
||||||
|
|
||||||
//place the key and value in the new array (reusing string memory)
|
//place the key and value in the new array (reusing string memory)
|
||||||
Toy_private_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
|
Toy_private_dictionary_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
|
||||||
|
|
||||||
entry->key = (*dictionaryHandle)[i].key;
|
entry->key = (*dictionaryHandle)[i].key;
|
||||||
entry->value = (*dictionaryHandle)[i].value;
|
entry->value = (*dictionaryHandle)[i].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//clear the old array
|
//clear the old array
|
||||||
TOY_FREE_ARRAY(Toy_private_entry, *dictionaryHandle, oldCapacity);
|
if (oldCapacity > 0) {
|
||||||
|
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
*dictionaryHandle = newEntries;
|
*dictionaryHandle = newEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
|
static bool setEntryArray(Toy_private_dictionary_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
|
||||||
//expand array if needed
|
//expand array if needed
|
||||||
if (contains + 1 > *capacityPtr * TOY_DICTIONARY_MAX_LOAD) {
|
if (contains + 1 > *capacityPtr * TOY_DICTIONARY_MAX_LOAD) {
|
||||||
int oldCapacity = *capacityPtr;
|
int oldCapacity = *capacityPtr;
|
||||||
@@ -82,7 +93,7 @@ static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr
|
|||||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_private_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
Toy_private_dictionary_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
||||||
|
|
||||||
//true = contains increase
|
//true = contains increase
|
||||||
if (TOY_IS_NULL(entry->key)) {
|
if (TOY_IS_NULL(entry->key)) {
|
||||||
@@ -97,14 +108,14 @@ static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeEntry(Toy_private_entry* entry) {
|
static void freeEntry(Toy_private_dictionary_entry* entry) {
|
||||||
Toy_freeLiteral(entry->key);
|
Toy_freeLiteral(entry->key);
|
||||||
Toy_freeLiteral(entry->value);
|
Toy_freeLiteral(entry->value);
|
||||||
entry->key = TOY_TO_NULL_LITERAL;
|
entry->key = TOY_TO_NULL_LITERAL;
|
||||||
entry->value = TOY_TO_NULL_LITERAL;
|
entry->value = TOY_TO_NULL_LITERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeEntryArray(Toy_private_entry* array, int capacity) {
|
static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
|
||||||
if (array == NULL) {
|
if (array == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -115,23 +126,24 @@ static void freeEntryArray(Toy_private_entry* array, int capacity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TOY_FREE_ARRAY(Toy_private_entry, array, capacity);
|
TOY_FREE_ARRAY(Toy_private_dictionary_entry, array, capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
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->entries = NULL;
|
||||||
dictionary->capacity = TOY_GROW_CAPACITY(0);
|
dictionary->capacity = 0;
|
||||||
dictionary->contains = 0;
|
dictionary->contains = 0;
|
||||||
dictionary->count = 0;
|
dictionary->count = 0;
|
||||||
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
|
dictionary->capacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
if (dictionary->capacity > 0) {
|
||||||
dictionary->capacity = 0;
|
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||||
dictionary->contains = 0;
|
dictionary->capacity = 0;
|
||||||
|
dictionary->contains = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
|
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
|
||||||
@@ -141,7 +153,7 @@ void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key
|
|||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
//BUGFIX: Can't hash a function
|
||||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
|
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key) || TOY_IS_FUNCTION_HOOK(key)) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (set)\n" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (set)\n" TOY_CC_RESET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -166,7 +178,7 @@ Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Lite
|
|||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
//BUGFIX: Can't hash a function
|
||||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
|
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key) || TOY_IS_FUNCTION_HOOK(key)) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (get)\n" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (get)\n" TOY_CC_RESET);
|
||||||
return TOY_TO_NULL_LITERAL;
|
return TOY_TO_NULL_LITERAL;
|
||||||
}
|
}
|
||||||
@@ -176,7 +188,7 @@ Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Lite
|
|||||||
return TOY_TO_NULL_LITERAL;
|
return TOY_TO_NULL_LITERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||||
|
|
||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
return Toy_copyLiteral(entry->value);
|
return Toy_copyLiteral(entry->value);
|
||||||
@@ -193,7 +205,7 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
|
|||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
//BUGFIX: Can't hash a function
|
||||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
|
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key) || TOY_IS_FUNCTION_HOOK(key)) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (remove)\n" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (remove)\n" TOY_CC_RESET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -203,7 +215,7 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||||
|
|
||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
freeEntry(entry);
|
freeEntry(entry);
|
||||||
@@ -214,6 +226,6 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
|
|||||||
|
|
||||||
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||||
//null & not tombstoned
|
//null & not tombstoned
|
||||||
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
|
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
|
||||||
return !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
|
return entry != NULL && !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,96 @@
|
|||||||
#pragma once
|
#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_common.h"
|
||||||
|
|
||||||
#include "toy_literal.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
|
//TODO: benchmark this
|
||||||
#define TOY_DICTIONARY_MAX_LOAD 0.75
|
#define TOY_DICTIONARY_MAX_LOAD 0.75
|
||||||
|
|
||||||
typedef struct Toy_private_entry {
|
typedef struct Toy_private_dictionary_entry {
|
||||||
Toy_Literal key;
|
Toy_Literal key;
|
||||||
Toy_Literal value;
|
Toy_Literal value;
|
||||||
} Toy_private_entry;
|
} Toy_private_dictionary_entry;
|
||||||
|
|
||||||
typedef struct Toy_LiteralDictionary {
|
typedef struct Toy_LiteralDictionary {
|
||||||
Toy_private_entry* entries;
|
Toy_private_dictionary_entry* entries;
|
||||||
int capacity;
|
int capacity;
|
||||||
int count;
|
int count;
|
||||||
int contains; //count + tombstones, for internal use
|
int contains; //count + tombstones, for internal use
|
||||||
} Toy_LiteralDictionary;
|
} 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);
|
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);
|
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_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);
|
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);
|
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);
|
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||||
|
|||||||
+7
-6
@@ -1,5 +1,6 @@
|
|||||||
#include "toy_memory.h"
|
#include "toy_memory.h"
|
||||||
#include "toy_refstring.h"
|
#include "toy_refstring.h"
|
||||||
|
#include "toy_reffunction.h"
|
||||||
|
|
||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
@@ -8,14 +9,13 @@
|
|||||||
|
|
||||||
//default allocator
|
//default allocator
|
||||||
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||||
if (newSize == 0 && oldSize == 0) {
|
//causes issues, so just skip out with a NO-OP (DISABLED for performance reasons)
|
||||||
//causes issues, so just skip out with a NO-OP
|
// if (newSize == 0 && oldSize == 0) {
|
||||||
return NULL;
|
// return NULL;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (newSize == 0) {
|
if (newSize == 0) {
|
||||||
free(pointer);
|
free(pointer);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t n
|
|||||||
|
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
|
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
|
||||||
exit(-1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mem;
|
return mem;
|
||||||
@@ -50,4 +50,5 @@ void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
|
|||||||
|
|
||||||
allocator = fn;
|
allocator = fn;
|
||||||
Toy_setRefStringAllocatorFn(fn);
|
Toy_setRefStringAllocatorFn(fn);
|
||||||
|
Toy_setRefFunctionAllocatorFn(fn);
|
||||||
}
|
}
|
||||||
|
|||||||
+104
-10
@@ -1,18 +1,112 @@
|
|||||||
#pragma once
|
#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"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
|
/*!
|
||||||
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
|
## Defined Macros
|
||||||
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
!*/
|
||||||
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
|
||||||
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
|
||||||
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
|
||||||
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
|
||||||
|
|
||||||
//implementation details
|
/*!
|
||||||
|
### 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))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## 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);
|
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);
|
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ typedef enum Toy_Opcode {
|
|||||||
TOY_OP_SCOPE_BEGIN,
|
TOY_OP_SCOPE_BEGIN,
|
||||||
TOY_OP_SCOPE_END,
|
TOY_OP_SCOPE_END,
|
||||||
|
|
||||||
TOY_OP_TYPE_DECL, //declare a type to be used (as a literal)
|
TOY_OP_TYPE_DECL_removed,
|
||||||
TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
TOY_OP_TYPE_DECL_LONG_removed,
|
||||||
|
|
||||||
TOY_OP_VAR_DECL, //declare a variable to be used (as a literal)
|
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)
|
TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
||||||
@@ -86,5 +86,9 @@ typedef enum Toy_Opcode {
|
|||||||
TOY_OP_FN_END, //different from SECTION_END
|
TOY_OP_FN_END, //different from SECTION_END
|
||||||
TOY_OP_SECTION_END = 255,
|
TOY_OP_SECTION_END = 255,
|
||||||
//TODO: add more
|
//TODO: add more
|
||||||
|
|
||||||
|
//prefix & postfix signals (used internally)
|
||||||
|
TOY_OP_PREFIX,
|
||||||
|
TOY_OP_POSTFIX,
|
||||||
} Toy_Opcode;
|
} Toy_Opcode;
|
||||||
|
|
||||||
|
|||||||
+119
-39
@@ -32,7 +32,7 @@ static void error(Toy_Parser* parser, Toy_Token token, const char* message) {
|
|||||||
|
|
||||||
static void advance(Toy_Parser* parser) {
|
static void advance(Toy_Parser* parser) {
|
||||||
parser->previous = parser->current;
|
parser->previous = parser->current;
|
||||||
parser->current = Toy_scanLexer(parser->lexer);
|
parser->current = Toy_private_scanLexer(parser->lexer);
|
||||||
|
|
||||||
if (parser->current.type == TOY_TOKEN_ERROR) {
|
if (parser->current.type == TOY_TOKEN_ERROR) {
|
||||||
error(parser, parser->current, "Toy_Lexer error");
|
error(parser, parser->current, "Toy_Lexer error");
|
||||||
@@ -119,6 +119,7 @@ ParseRule parseRules[];
|
|||||||
static void declaration(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
|
static void declaration(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
|
||||||
static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, PrecedenceRule rule);
|
static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, PrecedenceRule rule);
|
||||||
static Toy_Literal readTypeToLiteral(Toy_Parser* parser);
|
static Toy_Literal readTypeToLiteral(Toy_Parser* parser);
|
||||||
|
static void varDecl(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
|
||||||
|
|
||||||
//TODO: resolve the messy order of these
|
//TODO: resolve the messy order of these
|
||||||
//the expression rules
|
//the expression rules
|
||||||
@@ -140,7 +141,7 @@ static Toy_Opcode asType(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
|
|
||||||
static Toy_Opcode typeOf(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
static Toy_Opcode typeOf(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||||
Toy_ASTNode* rhs = NULL;
|
Toy_ASTNode* rhs = NULL;
|
||||||
parsePrecedence(parser, &rhs, PREC_TERNARY);
|
parsePrecedence(parser, &rhs, PREC_CALL);
|
||||||
Toy_emitASTNodeUnary(nodeHandle, TOY_OP_TYPE_OF, rhs);
|
Toy_emitASTNodeUnary(nodeHandle, TOY_OP_TYPE_OF, rhs);
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_EOF;
|
||||||
}
|
}
|
||||||
@@ -167,6 +168,10 @@ static Toy_Opcode compound(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
consume(parser, TOY_TOKEN_COMMA, "Expected ',' in array or dictionary");
|
consume(parser, TOY_TOKEN_COMMA, "Expected ',' in array or dictionary");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) { //allow for trailing commas
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
iterations++;
|
iterations++;
|
||||||
|
|
||||||
Toy_ASTNode* left = NULL;
|
Toy_ASTNode* left = NULL;
|
||||||
@@ -334,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) {
|
static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||||
advance(parser);
|
advance(parser);
|
||||||
|
|
||||||
@@ -341,102 +368,92 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
switch(parser->previous.type) {
|
switch(parser->previous.type) {
|
||||||
//arithmetic
|
//arithmetic
|
||||||
case TOY_TOKEN_PLUS: {
|
case TOY_TOKEN_PLUS: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_TERM);
|
parsePrecedence(parser, nodeHandle, PREC_TERM + 1);
|
||||||
return TOY_OP_ADDITION;
|
return TOY_OP_ADDITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_MINUS: {
|
case TOY_TOKEN_MINUS: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_TERM);
|
parsePrecedence(parser, nodeHandle, PREC_TERM + 1);
|
||||||
return TOY_OP_SUBTRACTION;
|
return TOY_OP_SUBTRACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_MULTIPLY: {
|
case TOY_TOKEN_MULTIPLY: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
|
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
|
||||||
return TOY_OP_MULTIPLICATION;
|
return TOY_OP_MULTIPLICATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_DIVIDE: {
|
case TOY_TOKEN_DIVIDE: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
|
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
|
||||||
return TOY_OP_DIVISION;
|
return TOY_OP_DIVISION;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_MODULO: {
|
case TOY_TOKEN_MODULO: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
|
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
|
||||||
return TOY_OP_MODULO;
|
return TOY_OP_MODULO;
|
||||||
}
|
}
|
||||||
|
|
||||||
//assignment
|
//assignment
|
||||||
case TOY_TOKEN_ASSIGN: {
|
case TOY_TOKEN_ASSIGN: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
|
||||||
return TOY_OP_VAR_ASSIGN;
|
return TOY_OP_VAR_ASSIGN;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_PLUS_ASSIGN: {
|
case TOY_TOKEN_PLUS_ASSIGN: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
|
||||||
return TOY_OP_VAR_ADDITION_ASSIGN;
|
return TOY_OP_VAR_ADDITION_ASSIGN;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_MINUS_ASSIGN: {
|
case TOY_TOKEN_MINUS_ASSIGN: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
|
||||||
return TOY_OP_VAR_SUBTRACTION_ASSIGN;
|
return TOY_OP_VAR_SUBTRACTION_ASSIGN;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_MULTIPLY_ASSIGN: {
|
case TOY_TOKEN_MULTIPLY_ASSIGN: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
|
||||||
return TOY_OP_VAR_MULTIPLICATION_ASSIGN;
|
return TOY_OP_VAR_MULTIPLICATION_ASSIGN;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_DIVIDE_ASSIGN: {
|
case TOY_TOKEN_DIVIDE_ASSIGN: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
|
||||||
return TOY_OP_VAR_DIVISION_ASSIGN;
|
return TOY_OP_VAR_DIVISION_ASSIGN;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_MODULO_ASSIGN: {
|
case TOY_TOKEN_MODULO_ASSIGN: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
|
||||||
return TOY_OP_VAR_MODULO_ASSIGN;
|
return TOY_OP_VAR_MODULO_ASSIGN;
|
||||||
}
|
}
|
||||||
|
|
||||||
//comparison
|
//comparison
|
||||||
case TOY_TOKEN_EQUAL: {
|
case TOY_TOKEN_EQUAL: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
|
||||||
return TOY_OP_COMPARE_EQUAL;
|
return TOY_OP_COMPARE_EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_NOT_EQUAL: {
|
case TOY_TOKEN_NOT_EQUAL: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
|
||||||
return TOY_OP_COMPARE_NOT_EQUAL;
|
return TOY_OP_COMPARE_NOT_EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_LESS: {
|
case TOY_TOKEN_LESS: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
|
||||||
return TOY_OP_COMPARE_LESS;
|
return TOY_OP_COMPARE_LESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_LESS_EQUAL: {
|
case TOY_TOKEN_LESS_EQUAL: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
|
||||||
return TOY_OP_COMPARE_LESS_EQUAL;
|
return TOY_OP_COMPARE_LESS_EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_GREATER: {
|
case TOY_TOKEN_GREATER: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
|
||||||
return TOY_OP_COMPARE_GREATER;
|
return TOY_OP_COMPARE_GREATER;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_GREATER_EQUAL: {
|
case TOY_TOKEN_GREATER_EQUAL: {
|
||||||
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
|
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
|
||||||
return TOY_OP_COMPARE_GREATER_EQUAL;
|
return TOY_OP_COMPARE_GREATER_EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOY_TOKEN_AND: {
|
|
||||||
parsePrecedence(parser, nodeHandle, PREC_AND);
|
|
||||||
return TOY_OP_AND;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TOY_TOKEN_OR: {
|
|
||||||
parsePrecedence(parser, nodeHandle, PREC_OR);
|
|
||||||
return TOY_OP_OR;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error(parser, parser->previous, "Unexpected token passed to binary precedence rule");
|
error(parser, parser->previous, "Unexpected token passed to binary precedence rule");
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_EOF;
|
||||||
@@ -627,6 +644,14 @@ static Toy_Opcode castingPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
//BUGFIX: handle this here, and not in castingPrefix, so "any" can be recognized as a type properly
|
||||||
|
case TOY_TOKEN_ANY: {
|
||||||
|
Toy_Literal literal = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, false);
|
||||||
|
Toy_emitASTNodeLiteral(nodeHandle, literal);
|
||||||
|
Toy_freeLiteral(literal);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error(parser, parser->previous, "Unexpected token passed to casting precedence rule");
|
error(parser, parser->previous, "Unexpected token passed to casting precedence rule");
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_EOF;
|
||||||
@@ -658,7 +683,7 @@ static Toy_Opcode castingInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_TOKEN_LITERAL_STRING:
|
case TOY_TOKEN_LITERAL_STRING:
|
||||||
atomic(parser, nodeHandle);
|
string(parser, nodeHandle);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -684,7 +709,7 @@ static Toy_Opcode incrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle)
|
|||||||
|
|
||||||
Toy_freeASTNode(tmpNode);
|
Toy_freeASTNode(tmpNode);
|
||||||
|
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Opcode incrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
static Toy_Opcode incrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||||
@@ -701,7 +726,7 @@ static Toy_Opcode incrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
|
|
||||||
Toy_freeASTNode(tmpNode);
|
Toy_freeASTNode(tmpNode);
|
||||||
|
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_POSTFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Opcode decrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
static Toy_Opcode decrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||||
@@ -718,7 +743,7 @@ static Toy_Opcode decrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle)
|
|||||||
|
|
||||||
Toy_freeASTNode(tmpNode);
|
Toy_freeASTNode(tmpNode);
|
||||||
|
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||||
@@ -735,10 +760,18 @@ static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
|
|
||||||
Toy_freeASTNode(tmpNode);
|
Toy_freeASTNode(tmpNode);
|
||||||
|
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_POSTFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Opcode fnCall(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
static Toy_Opcode fnCall(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
||||||
|
//wait - is the previous token a type? this should be casting instead
|
||||||
|
if (parser->previous.type >= TOY_TOKEN_NULL && parser->previous.type <= TOY_TOKEN_ANY) {
|
||||||
|
//casting value
|
||||||
|
parsePrecedence(parser, nodeHandle, PREC_CALL);
|
||||||
|
|
||||||
|
return TOY_OP_TYPE_CAST; //opcode value
|
||||||
|
}
|
||||||
|
|
||||||
advance(parser); //skip the left paren
|
advance(parser); //skip the left paren
|
||||||
|
|
||||||
//binary() is an infix rule - so only get the RHS of the operator
|
//binary() is an infix rule - so only get the RHS of the operator
|
||||||
@@ -810,13 +843,20 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
|
|||||||
//eat the first
|
//eat the first
|
||||||
if (!match(parser, TOY_TOKEN_COLON)) {
|
if (!match(parser, TOY_TOKEN_COLON)) {
|
||||||
Toy_freeASTNode(first);
|
Toy_freeASTNode(first);
|
||||||
|
first = NULL;
|
||||||
parsePrecedence(parser, &first, PREC_TERNARY);
|
parsePrecedence(parser, &first, PREC_TERNARY);
|
||||||
match(parser, TOY_TOKEN_COLON);
|
match(parser, TOY_TOKEN_COLON);
|
||||||
readFirst = true;
|
readFirst = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
|
if (!first) {
|
||||||
|
Toy_freeASTNode(first);
|
||||||
|
Toy_freeASTNode(second);
|
||||||
|
Toy_freeASTNode(third);
|
||||||
|
return TOY_OP_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
|
||||||
if (readFirst) {
|
if (readFirst) {
|
||||||
Toy_freeASTNode(second);
|
Toy_freeASTNode(second);
|
||||||
second = NULL;
|
second = NULL;
|
||||||
@@ -832,10 +872,18 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
|
|||||||
//eat the second
|
//eat the second
|
||||||
if (!match(parser, TOY_TOKEN_COLON)) {
|
if (!match(parser, TOY_TOKEN_COLON)) {
|
||||||
Toy_freeASTNode(second);
|
Toy_freeASTNode(second);
|
||||||
|
second = NULL;
|
||||||
parsePrecedence(parser, &second, PREC_TERNARY);
|
parsePrecedence(parser, &second, PREC_TERNARY);
|
||||||
match(parser, TOY_TOKEN_COLON);
|
match(parser, TOY_TOKEN_COLON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!second) {
|
||||||
|
Toy_freeASTNode(first);
|
||||||
|
Toy_freeASTNode(second);
|
||||||
|
Toy_freeASTNode(third);
|
||||||
|
return TOY_OP_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
|
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
|
||||||
Toy_freeASTNode(third);
|
Toy_freeASTNode(third);
|
||||||
third = NULL;
|
third = NULL;
|
||||||
@@ -845,7 +893,16 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
|
|||||||
|
|
||||||
//eat the third
|
//eat the third
|
||||||
Toy_freeASTNode(third);
|
Toy_freeASTNode(third);
|
||||||
|
third = NULL;
|
||||||
parsePrecedence(parser, &third, PREC_TERNARY);
|
parsePrecedence(parser, &third, PREC_TERNARY);
|
||||||
|
|
||||||
|
if (!third) {
|
||||||
|
Toy_freeASTNode(first);
|
||||||
|
Toy_freeASTNode(second);
|
||||||
|
Toy_freeASTNode(third);
|
||||||
|
return TOY_OP_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
Toy_emitASTNodeIndex(nodeHandle, first, second, third);
|
Toy_emitASTNodeIndex(nodeHandle, first, second, third);
|
||||||
|
|
||||||
consume(parser, TOY_TOKEN_BRACKET_RIGHT, "Expected ']' in index notation");
|
consume(parser, TOY_TOKEN_BRACKET_RIGHT, "Expected ']' in index notation");
|
||||||
@@ -894,7 +951,7 @@ ParseRule parseRules[] = { //must match the token types
|
|||||||
{NULL, NULL, PREC_NONE},// TOKEN_DICTIONARY,
|
{NULL, NULL, PREC_NONE},// TOKEN_DICTIONARY,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_FUNCTION,
|
{NULL, NULL, PREC_NONE},// TOKEN_FUNCTION,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_OPAQUE,
|
{NULL, NULL, PREC_NONE},// TOKEN_OPAQUE,
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_ANY,
|
{castingPrefix, NULL, PREC_CALL},// TOKEN_ANY,
|
||||||
|
|
||||||
//keywords and reserved words
|
//keywords and reserved words
|
||||||
{NULL, NULL, PREC_NONE},// TOKEN_AS,
|
{NULL, NULL, PREC_NONE},// TOKEN_AS,
|
||||||
@@ -957,8 +1014,8 @@ ParseRule parseRules[] = { //must match the token types
|
|||||||
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER,
|
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER,
|
||||||
{NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL,
|
{NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL,
|
||||||
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL,
|
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL,
|
||||||
{NULL, binary, PREC_AND},// TOKEN_AND,
|
{NULL, circuit, PREC_AND},// TOKEN_AND,
|
||||||
{NULL, binary, PREC_OR},// TOKEN_OR,
|
{NULL, circuit, PREC_OR},// TOKEN_OR,
|
||||||
|
|
||||||
//other operators
|
//other operators
|
||||||
{NULL, question, PREC_TERNARY}, //TOKEN_QUESTION,
|
{NULL, question, PREC_TERNARY}, //TOKEN_QUESTION,
|
||||||
@@ -1233,6 +1290,23 @@ static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, Preced
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BUGFIX: keep going, don't skip out on a postfix
|
||||||
|
if (opcode == TOY_OP_PREFIX || opcode == TOY_OP_POSTFIX) {
|
||||||
|
Toy_freeASTNode(*nodeHandle);
|
||||||
|
*nodeHandle = rhsNode;
|
||||||
|
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);
|
Toy_emitASTNodeBinary(nodeHandle, rhsNode, opcode);
|
||||||
|
|
||||||
//optimise away the constants
|
//optimise away the constants
|
||||||
@@ -1352,7 +1426,13 @@ static void forStmt(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
|
|
||||||
//check the pre-clause
|
//check the pre-clause
|
||||||
if (parser->current.type != TOY_TOKEN_SEMICOLON) {
|
if (parser->current.type != TOY_TOKEN_SEMICOLON) {
|
||||||
declaration(parser, &preClause); //allow defining variables in the pre-clause
|
//allow defining variables in the pre-clause
|
||||||
|
if (match(parser, TOY_TOKEN_VAR)) {
|
||||||
|
varDecl(parser, &preClause);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parsePrecedence(parser, &preClause, PREC_ASSIGNMENT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after empty declaration of for clause");
|
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after empty declaration of for clause");
|
||||||
|
|||||||
+84
-1
@@ -1,10 +1,62 @@
|
|||||||
#pragma once
|
#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_common.h"
|
||||||
#include "toy_lexer.h"
|
#include "toy_lexer.h"
|
||||||
#include "toy_ast_node.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 {
|
typedef struct {
|
||||||
Toy_Lexer* lexer;
|
Toy_Lexer* lexer;
|
||||||
bool error; //I've had an error
|
bool error; //I've had an error
|
||||||
@@ -15,6 +67,37 @@ typedef struct {
|
|||||||
Toy_Token previous;
|
Toy_Token previous;
|
||||||
} Toy_Parser;
|
} 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);
|
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_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);
|
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.
|
||||||
|
!*/
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include "toy_reffunction.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//memory allocation
|
||||||
|
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
static Toy_RefFunctionAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||||
|
|
||||||
|
void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn allocator) {
|
||||||
|
allocate = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
//API
|
||||||
|
Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length) {
|
||||||
|
//allocate the memory area (including metadata space)
|
||||||
|
Toy_RefFunction* refFunction = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * length);
|
||||||
|
|
||||||
|
if (refFunction == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the data
|
||||||
|
refFunction->refCount = 1;
|
||||||
|
refFunction->length = length;
|
||||||
|
memcpy(refFunction->data, data, refFunction->length);
|
||||||
|
|
||||||
|
return refFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_deleteRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
//decrement, then check
|
||||||
|
refFunction->refCount--;
|
||||||
|
if (refFunction->refCount <= 0) {
|
||||||
|
allocate(refFunction, sizeof(size_t) + sizeof(int) + sizeof(char) * (refFunction->length + 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Toy_countRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
return refFunction->refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
return refFunction->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
//Cheaty McCheater Face
|
||||||
|
refFunction->refCount++;
|
||||||
|
return refFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
//create a new function, with a new refCount
|
||||||
|
return Toy_createRefFunction(refFunction->data, refFunction->length);
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_reffunction.h
|
||||||
|
|
||||||
|
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 {
|
||||||
|
size_t length;
|
||||||
|
int refCount;
|
||||||
|
unsigned char data[];
|
||||||
|
} Toy_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);
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "toy_refstring.h"
|
#include "toy_refstring.h"
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
//memory allocation
|
//memory allocation
|
||||||
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||||
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||||
|
|||||||
+102
-6
@@ -1,13 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdbool.h>
|
/*!
|
||||||
#include <stddef.h>
|
# 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 "toy_common.h"
|
||||||
|
|
||||||
//memory allocation hook
|
#include <string.h>
|
||||||
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
|
||||||
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
|
|
||||||
|
|
||||||
//the RefString structure
|
//the RefString structure
|
||||||
typedef struct Toy_RefString {
|
typedef struct Toy_RefString {
|
||||||
@@ -16,14 +26,100 @@ typedef struct Toy_RefString {
|
|||||||
char data[];
|
char data[];
|
||||||
} Toy_RefString;
|
} 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_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);
|
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);
|
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);
|
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_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_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);
|
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);
|
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);
|
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);
|
TOY_API bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
|
||||||
|
|
||||||
|
//TODO: merge refstring memory
|
||||||
|
|||||||
+65
-63
@@ -4,21 +4,19 @@
|
|||||||
|
|
||||||
//run up the ancestor chain, freeing anything with 0 references left
|
//run up the ancestor chain, freeing anything with 0 references left
|
||||||
static void freeAncestorChain(Toy_Scope* scope) {
|
static void freeAncestorChain(Toy_Scope* scope) {
|
||||||
scope->references--;
|
while (scope != NULL) {
|
||||||
|
Toy_Scope* next = scope->ancestor;
|
||||||
|
|
||||||
//free scope chain
|
scope->references--;
|
||||||
if (scope->ancestor != NULL) {
|
|
||||||
freeAncestorChain(scope->ancestor);
|
if (scope->references <= 0) {
|
||||||
|
Toy_freeLiteralDictionary(&scope->variables);
|
||||||
|
Toy_freeLiteralDictionary(&scope->types);
|
||||||
|
TOY_FREE(Toy_Scope, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scope->references > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_freeLiteralDictionary(&scope->variables);
|
|
||||||
Toy_freeLiteralDictionary(&scope->types);
|
|
||||||
|
|
||||||
TOY_FREE(Toy_Scope, scope);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//return false if invalid type
|
//return false if invalid type
|
||||||
@@ -126,7 +124,7 @@ static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal
|
|||||||
}
|
}
|
||||||
|
|
||||||
//find the internal child of original that matches this child of value
|
//find the internal child of original that matches this child of value
|
||||||
Toy_private_entry* ptr = NULL;
|
Toy_private_dictionary_entry* ptr = NULL;
|
||||||
|
|
||||||
for (int j = 0; j < TOY_AS_DICTIONARY(original)->capacity; j++) {
|
for (int j = 0; j < TOY_AS_DICTIONARY(original)->capacity; j++) {
|
||||||
if (Toy_literalsAreEqual(TOY_AS_DICTIONARY(original)->entries[j].key, TOY_AS_DICTIONARY(value)->entries[i].key)) {
|
if (Toy_literalsAreEqual(TOY_AS_DICTIONARY(original)->entries[j].key, TOY_AS_DICTIONARY(value)->entries[i].key)) {
|
||||||
@@ -159,6 +157,10 @@ static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_OPAQUE && !TOY_IS_OPAQUE(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +187,7 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
|||||||
|
|
||||||
Toy_Scope* ret = scope->ancestor;
|
Toy_Scope* ret = scope->ancestor;
|
||||||
|
|
||||||
//BUGFIX: when freeing a scope, free the function's scopes manually
|
//BUGFIX: when freeing a scope, free the functions' scopes manually - I *think* this is related to the closure hack-in
|
||||||
for (int i = 0; i < scope->variables.capacity; i++) {
|
for (int i = 0; i < scope->variables.capacity; i++) {
|
||||||
//handle keys, just in case
|
//handle keys, just in case
|
||||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
|
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
|
||||||
@@ -205,6 +207,10 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
|
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
|
||||||
|
if (original == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||||
scope->ancestor = original->ancestor;
|
scope->ancestor = original->ancestor;
|
||||||
Toy_initLiteralDictionary(&scope->variables);
|
Toy_initLiteralDictionary(&scope->variables);
|
||||||
@@ -250,75 +256,71 @@ bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal typ
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
|
bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
|
||||||
if (scope == NULL) {
|
while (scope != NULL) {
|
||||||
return false;
|
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = scope->ancestor;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
return false;
|
||||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
|
||||||
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//return false if undefined, or can't be assigned
|
//return false if undefined, or can't be assigned
|
||||||
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
|
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
|
||||||
//dead end
|
while (scope != NULL) {
|
||||||
if (scope == NULL) {
|
//if it's not in this scope, keep searching up the chain
|
||||||
return false;
|
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||||
}
|
scope = scope->ancestor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
//type checking
|
||||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
|
||||||
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
|
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
|
||||||
}
|
|
||||||
|
|
||||||
//type checking
|
if (!checkType(typeLiteral, original, value, constCheck)) {
|
||||||
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
|
Toy_freeLiteral(typeLiteral);
|
||||||
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
|
Toy_freeLiteral(original);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//actually assign
|
||||||
|
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
|
||||||
|
|
||||||
if (!checkType(typeLiteral, original, value, constCheck)) {
|
|
||||||
Toy_freeLiteral(typeLiteral);
|
Toy_freeLiteral(typeLiteral);
|
||||||
Toy_freeLiteral(original);
|
Toy_freeLiteral(original);
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//actually assign
|
return false;
|
||||||
Toy_setLiteralDictionary(&scope->variables, key, value);
|
|
||||||
|
|
||||||
Toy_freeLiteral(typeLiteral);
|
|
||||||
Toy_freeLiteral(original);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
|
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
|
||||||
//dead end
|
//optimized to reduce call stack
|
||||||
if (scope == NULL) {
|
while (scope != NULL) {
|
||||||
return false;
|
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||||
|
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = scope->ancestor;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
return false;
|
||||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
|
||||||
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
|
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
|
||||||
//dead end
|
while (scope != NULL) {
|
||||||
if (scope == NULL) {
|
if (Toy_existsLiteralDictionary(&scope->types, key)) {
|
||||||
return TOY_TO_NULL_LITERAL;
|
return Toy_getLiteralDictionary(&scope->types, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = scope->ancestor;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
return TOY_TO_NULL_LITERAL;
|
||||||
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
|
|
||||||
return Toy_getScopeType(scope->ancestor, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Toy_getLiteralDictionary(&scope->types, key);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-4
@@ -1,5 +1,16 @@
|
|||||||
#pragma once
|
#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_array.h"
|
||||||
#include "toy_literal_dictionary.h"
|
#include "toy_literal_dictionary.h"
|
||||||
|
|
||||||
@@ -10,16 +21,70 @@ typedef struct Toy_Scope {
|
|||||||
int references; //how many scopes point here
|
int references; //how many scopes point here
|
||||||
} Toy_Scope;
|
} 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_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_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);
|
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);
|
### bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type)
|
||||||
TOY_API bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
|
|
||||||
|
|
||||||
//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);
|
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_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);
|
TOY_API Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ typedef enum Toy_TokenType {
|
|||||||
TOY_TOKEN_GREATER,
|
TOY_TOKEN_GREATER,
|
||||||
TOY_TOKEN_LESS_EQUAL,
|
TOY_TOKEN_LESS_EQUAL,
|
||||||
TOY_TOKEN_GREATER_EQUAL,
|
TOY_TOKEN_GREATER_EQUAL,
|
||||||
TOY_TOKEN_AND,
|
TOY_TOKEN_AND_AND,
|
||||||
TOY_TOKEN_OR,
|
TOY_TOKEN_OR_OR,
|
||||||
|
|
||||||
//other operators
|
//other operators
|
||||||
TOY_TOKEN_QUESTION,
|
TOY_TOKEN_QUESTION,
|
||||||
|
|||||||
+3
-3
@@ -2,9 +2,9 @@ CC=gcc
|
|||||||
|
|
||||||
IDIR +=. ../source ../repl
|
IDIR +=. ../source ../repl
|
||||||
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||||
LIBS +=
|
LIBS +=-lm
|
||||||
ODIR = obj
|
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)
|
TESTS = $(wildcard test_*.c)
|
||||||
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
|
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
|||||||
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
|
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
|
||||||
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
|
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
|
||||||
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
|
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
|
||||||
valgrind --leak-check=full --track-origins=yes $@
|
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all $@
|
||||||
else
|
else
|
||||||
$@
|
$@
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -38,5 +38,12 @@ s += "bar";
|
|||||||
|
|
||||||
assert s == "foobar", "string addition failed (wasn't sticky enough)";
|
assert s == "foobar", "string addition failed (wasn't sticky enough)";
|
||||||
|
|
||||||
|
//check order of operations
|
||||||
|
assert 30 / 3 * 2 == 20, "Order of operations failed (raw numbers)";
|
||||||
|
var x = 30;
|
||||||
|
var y = 3;
|
||||||
|
var z = 2;
|
||||||
|
assert x / y * z == 20, "Order of operations failed (variables)";
|
||||||
|
|
||||||
|
|
||||||
print "All good";
|
print "All good";
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
var s = "42";
|
||||||
|
var t = "69";
|
||||||
|
|
||||||
|
assert int (s + t) - 1 == 4268, "casting parentheses failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -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";
|
print "All good";
|
||||||
|
|||||||
@@ -23,5 +23,8 @@ assert !false, "!false";
|
|||||||
var c = false;
|
var c = false;
|
||||||
assert !c, "!c";
|
assert !c, "!c";
|
||||||
|
|
||||||
|
//test multiple comparisons
|
||||||
|
assert 1 == 2 == false, "Left-accociative equality failed";
|
||||||
|
|
||||||
|
|
||||||
print "All good";
|
print "All good";
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
var days = [
|
||||||
|
"sunday",
|
||||||
|
"monday",
|
||||||
|
"tuesday",
|
||||||
|
"wednesday",
|
||||||
|
"thursday",
|
||||||
|
"friday",
|
||||||
|
"saturday"
|
||||||
|
];
|
||||||
|
|
||||||
|
var rng = 10; //for chosen at random
|
||||||
|
|
||||||
|
|
||||||
|
var index = rng % days.length();
|
||||||
|
|
||||||
|
assert index == 3, "dot modulo bugfix failed";
|
||||||
|
|
||||||
|
rng %= days.length();
|
||||||
|
|
||||||
|
assert rng == 3, "dot modulo assign bugfix failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
fn a() {
|
||||||
|
fn b() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert a()() == 42, "function within function failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
fn a() {
|
||||||
|
fn b() {
|
||||||
|
fn c() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert a()()() == 42, "function within function within function failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,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,15 @@
|
|||||||
|
var a = 0;
|
||||||
|
|
||||||
|
if (a++ >= 1) {
|
||||||
|
assert false, "increment postfix bugfix failed (first check)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (a++ >= 1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert a == 2, "increment postfix bugfix failed (second check)";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -92,4 +92,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test deep-combine example
|
||||||
|
{
|
||||||
|
fn combine(a, b, c, d) {
|
||||||
|
return [[a, b], [c, d]];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert combine(1, 2, 3, 4) == [[1, 2], [3, 4]], "deep-combine example failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
print "All good";
|
print "All good";
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
var result; //result must exist to ensure assingment, rather than declaration is invoked by the comparison below
|
||||||
|
|
||||||
|
var lhs = [0];
|
||||||
|
var rhs = [0];
|
||||||
|
|
||||||
|
result = lhs[0] < rhs[0]; //make sure this doesn't invoke TOY_OP_INDEX_ASSIGN_INTERMEDIATE
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,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";
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import standard;
|
||||||
|
import random;
|
||||||
|
|
||||||
|
var generator: opaque = createRandomGenerator(clock().hash()); //create a new generator object, from a non-determinant source
|
||||||
|
|
||||||
|
var a: int = generator.generateRandomNumber();
|
||||||
|
var b: int = generator.generateRandomNumber();
|
||||||
|
var c: int = generator.generateRandomNumber();
|
||||||
|
|
||||||
|
generator.freeRandomGenerator();
|
||||||
|
|
||||||
|
assert a != b, "random a != random b failed";
|
||||||
|
assert a != c, "random a != random c failed";
|
||||||
|
assert b != c, "random b != random c failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
Binary file not shown.
@@ -7,6 +7,157 @@ import standard;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test hash
|
||||||
|
{
|
||||||
|
assert typeof "Hello world".hash() == int, "typeof \"Hello world\".hash() failed";
|
||||||
|
assert "Hello world".hash() == 994097935, "\"Hello world\".hash() failed"; //NOTE: specific value based on algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test abs
|
||||||
|
{
|
||||||
|
assert abs(-5) == 5, "abs(-integer) failed";
|
||||||
|
assert abs(-5.5) == 5.5, "abs(-float) failed";
|
||||||
|
assert abs(5) == 5, "abs(+integer) failed";
|
||||||
|
assert abs(5.5) == 5.5, "abs(+float) failed";
|
||||||
|
|
||||||
|
var x = -5;
|
||||||
|
|
||||||
|
assert x.abs() == 5, "var.abs() failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test ceil
|
||||||
|
{
|
||||||
|
assert ceil(4) == 4, "ceil(int) failed";
|
||||||
|
assert ceil(4.0) == 4, "ceil(float) failed";
|
||||||
|
assert ceil(4.1) == 5, "ceil() failed";
|
||||||
|
|
||||||
|
var x = 4.1;
|
||||||
|
|
||||||
|
assert x.ceil() == 5, "var.ceil() failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test floor
|
||||||
|
{
|
||||||
|
assert floor(4) == 4, "floor(int) failed";
|
||||||
|
assert floor(4.0) == 4, "floor(float) failed";
|
||||||
|
assert floor(4.1) == 4, "floor() failed";
|
||||||
|
|
||||||
|
var x = 4.1;
|
||||||
|
|
||||||
|
assert x.floor() == 4, "var.floor() failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test max
|
||||||
|
{
|
||||||
|
assert max(1, 2, 3) == 3, "max() failed";
|
||||||
|
|
||||||
|
var a = 1;
|
||||||
|
var b = 2;
|
||||||
|
var c = 3;
|
||||||
|
|
||||||
|
assert max(a, b, c) == 3, "var.max() failed";
|
||||||
|
|
||||||
|
assert max(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) == 9, "max() with many args failed";
|
||||||
|
|
||||||
|
assert typeof max(1, 2, 3) == int, "typeof max() == int failed";
|
||||||
|
assert typeof max(1, 2, 3.4) == float, "typeof max() == float failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test min
|
||||||
|
{
|
||||||
|
assert min(1, 2, 3) == 1, "min() failed";
|
||||||
|
|
||||||
|
var a = 1;
|
||||||
|
var b = 2;
|
||||||
|
var c = 3;
|
||||||
|
|
||||||
|
assert min(a, b, c) == 1, "var.min() failed";
|
||||||
|
|
||||||
|
assert min(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) == 0, "min() with many args failed";
|
||||||
|
|
||||||
|
assert typeof min(1, 2, 3) == int, "typeof min() == int failed";
|
||||||
|
assert typeof min(1, 2, 3.4) == float, "typeof min() == float failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test round
|
||||||
|
{
|
||||||
|
assert round(4) == 4, "round(int) failed";
|
||||||
|
assert round(4.0) == 4, "round(float) failed";
|
||||||
|
assert round(4.1) == 4, "round(less than half) failed";
|
||||||
|
assert round(4.9) == 5, "round(greater than half) failed";
|
||||||
|
assert round(4.5) == 5, "round(exactly half) failed";
|
||||||
|
|
||||||
|
var x = 4.1;
|
||||||
|
|
||||||
|
assert x.round() == 4, "var.round() failed";
|
||||||
|
|
||||||
|
assert typeof round(1.0) == int, "typeof round() == int failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test 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 concat
|
||||||
{
|
{
|
||||||
//test array concat
|
//test array concat
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import about as about;
|
import toy_version_info as toy_version_info;
|
||||||
import about;
|
import toy_version_info;
|
||||||
|
|
||||||
assert author == "Kayne Ruse, KR Game Studios", "Author failed";
|
assert author == "Kayne Ruse, KR Game Studios", "Author failed";
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"a"[--];
|
||||||
@@ -1,12 +1,21 @@
|
|||||||
fn fib(n : int) {
|
//memoize the fib function
|
||||||
if (n < 2) {
|
var memo: [int : int] = [:];
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fib(n-1) + fib(n-2);
|
fn fib(n : int) {
|
||||||
|
if (n < 2) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = memo[n];
|
||||||
|
if (result == null) {
|
||||||
|
result = fib(n-1) + fib(n-2);
|
||||||
|
memo[n] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 20; i++) {
|
for (var i = 0; i < 40; i++) {
|
||||||
var res = fib(i);
|
var res = fib(i);
|
||||||
print string i + ": " + string res;
|
print string i + ": " + string res;
|
||||||
}
|
}
|
||||||
@@ -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";
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
var array = [
|
||||||
|
1, 2, 3,
|
||||||
|
4, 5, 6,
|
||||||
|
7, 8, 9, //explicitly leave a trailing comma
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -22,4 +22,9 @@ var dict: complex = [
|
|||||||
"third array": [7, 8, 9]
|
"third array": [7, 8, 9]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
//check the any type is recognized as a type within an array
|
||||||
|
var a: [type] = [int, bool, any];
|
||||||
|
|
||||||
|
|
||||||
print "All good";
|
print "All good";
|
||||||
|
|||||||
+26
-26
@@ -20,14 +20,14 @@ static void noPrintFn(const char* output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void error(char* msg) {
|
void error(char* msg) {
|
||||||
printf("%s", msg);
|
printf("%s\n", msg);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const char* source = Toy_readFile("scripts/call-from-host.toy", &size);
|
const char* source = (const char*)Toy_readFile("scripts/call-from-host.toy", &size);
|
||||||
const unsigned char* tb = Toy_compileString(source, &size);
|
const unsigned char* tb = Toy_compileString(source, &size);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ int main() {
|
|||||||
|
|
||||||
//test answer
|
//test answer
|
||||||
{
|
{
|
||||||
interpreter.printOutput("Testing answer\n");
|
interpreter.printOutput("Testing answer");
|
||||||
|
|
||||||
Toy_LiteralArray arguments;
|
Toy_LiteralArray arguments;
|
||||||
Toy_initLiteralArray(&arguments);
|
Toy_initLiteralArray(&arguments);
|
||||||
@@ -52,15 +52,15 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments has the wrong number of members\n");
|
error("Arguments has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1) {
|
if (returns.count != 1) {
|
||||||
error("Returns has the wrong number of members\n");
|
error("Returns has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 42) {
|
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 42) {
|
||||||
error("Returned value is incorrect\n");
|
error("Returned value is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_freeLiteralArray(&arguments);
|
Toy_freeLiteralArray(&arguments);
|
||||||
@@ -69,7 +69,7 @@ int main() {
|
|||||||
|
|
||||||
//test identity
|
//test identity
|
||||||
{
|
{
|
||||||
interpreter.printOutput("Testing identity\n");
|
interpreter.printOutput("Testing identity");
|
||||||
|
|
||||||
Toy_LiteralArray arguments;
|
Toy_LiteralArray arguments;
|
||||||
Toy_initLiteralArray(&arguments);
|
Toy_initLiteralArray(&arguments);
|
||||||
@@ -85,17 +85,17 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments has the wrong number of members\n");
|
error("Arguments has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1) {
|
if (returns.count != 1) {
|
||||||
error("Returns has the wrong number of members\n");
|
error("Returns has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
float epsilon = 0.1; //because floats are evil
|
float epsilon = 0.1; //because floats are evil
|
||||||
|
|
||||||
if (!TOY_IS_FLOAT(returns.literals[0]) || fabs(TOY_AS_FLOAT(returns.literals[0]) - pi) > epsilon) {
|
if (!TOY_IS_FLOAT(returns.literals[0]) || fabs(TOY_AS_FLOAT(returns.literals[0]) - pi) > epsilon) {
|
||||||
error("Returned value is incorrect\n");
|
error("Returned value is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_freeLiteralArray(&arguments);
|
Toy_freeLiteralArray(&arguments);
|
||||||
@@ -104,7 +104,7 @@ int main() {
|
|||||||
|
|
||||||
//test makeCounter (closures)
|
//test makeCounter (closures)
|
||||||
{
|
{
|
||||||
interpreter.printOutput("Testing makeCounter (closures)\n");
|
interpreter.printOutput("Testing makeCounter (closures)");
|
||||||
|
|
||||||
Toy_LiteralArray arguments;
|
Toy_LiteralArray arguments;
|
||||||
Toy_initLiteralArray(&arguments);
|
Toy_initLiteralArray(&arguments);
|
||||||
@@ -115,11 +115,11 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments has the wrong number of members\n");
|
error("Arguments has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1) {
|
if (returns.count != 1) {
|
||||||
error("Returns has the wrong number of members\n");
|
error("Returns has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
//grab the resulting literal
|
//grab the resulting literal
|
||||||
@@ -139,15 +139,15 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments (1) has the wrong number of members\n");
|
error("Arguments (1) has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1) {
|
if (returns.count != 1) {
|
||||||
error("Returns (1) has the wrong number of members\n");
|
error("Returns (1) has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 1) {
|
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 1) {
|
||||||
error("Returned value (1) is incorrect\n");
|
error("Returned value (1) is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_freeLiteralArray(&arguments);
|
Toy_freeLiteralArray(&arguments);
|
||||||
@@ -164,15 +164,15 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments (2) has the wrong number of members\n");
|
error("Arguments (2) has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1) {
|
if (returns.count != 1) {
|
||||||
error("Returns (2) has the wrong number of members\n");
|
error("Returns (2) has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 2) {
|
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 2) {
|
||||||
error("Returned value (2) is incorrect\n");
|
error("Returned value (2) is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_freeLiteralArray(&arguments);
|
Toy_freeLiteralArray(&arguments);
|
||||||
@@ -189,15 +189,15 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments (3) has the wrong number of members\n");
|
error("Arguments (3) has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1) {
|
if (returns.count != 1) {
|
||||||
error("Returns (3) has the wrong number of members\n");
|
error("Returns (3) has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 3) {
|
if (!TOY_IS_INTEGER(returns.literals[0]) || TOY_AS_INTEGER(returns.literals[0]) != 3) {
|
||||||
error("Returned value (3) is incorrect\n");
|
error("Returned value (3) is incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_freeLiteralArray(&arguments);
|
Toy_freeLiteralArray(&arguments);
|
||||||
@@ -209,7 +209,7 @@ int main() {
|
|||||||
|
|
||||||
//test assertion failure
|
//test assertion failure
|
||||||
{
|
{
|
||||||
interpreter.printOutput("Testing assertion failure\n");
|
interpreter.printOutput("Testing assertion failure");
|
||||||
|
|
||||||
Toy_setInterpreterAssert(&interpreter, noPrintFn);
|
Toy_setInterpreterAssert(&interpreter, noPrintFn);
|
||||||
|
|
||||||
@@ -222,15 +222,15 @@ int main() {
|
|||||||
|
|
||||||
//check the results
|
//check the results
|
||||||
if (arguments.count != 0) {
|
if (arguments.count != 0) {
|
||||||
error("Arguments has the wrong number of members\n");
|
error("Arguments has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returns.count != 1 || !TOY_IS_NULL(returns.literals[0])) {
|
if (returns.count != 1 || !TOY_IS_NULL(returns.literals[0])) {
|
||||||
error("Returns has the wrong number of members\n");
|
error("Returns has the wrong number of members");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
error("Assertion gives the wrong return value\n");
|
error("Assertion gives the wrong return value");
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_freeLiteralArray(&arguments);
|
Toy_freeLiteralArray(&arguments);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ int main() {
|
|||||||
Toy_writeCompiler(&compiler, node);
|
Toy_writeCompiler(&compiler, node);
|
||||||
|
|
||||||
//collate
|
//collate
|
||||||
int size = 0;
|
size_t size = 0;
|
||||||
unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
@@ -52,7 +52,7 @@ int main() {
|
|||||||
{
|
{
|
||||||
//source
|
//source
|
||||||
size_t sourceLength = 0;
|
size_t sourceLength = 0;
|
||||||
const char* source = Toy_readFile("scripts/compiler_sample_code.toy", &sourceLength);
|
const char* source = (const char*)Toy_readFile("scripts/compiler_sample_code.toy", &sourceLength);
|
||||||
|
|
||||||
//test basic compilation & collation
|
//test basic compilation & collation
|
||||||
Toy_Lexer lexer;
|
Toy_Lexer lexer;
|
||||||
@@ -78,7 +78,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//collate
|
//collate
|
||||||
int size = 0;
|
size_t size = 0;
|
||||||
unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
+18
-4
@@ -18,6 +18,7 @@ static void noPrintFn(const char* output) {
|
|||||||
//NO OP
|
//NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int failedAssertions = 0;
|
||||||
int ignoredAssertions = 0;
|
int ignoredAssertions = 0;
|
||||||
static void noAssertFn(const char* output) {
|
static void noAssertFn(const char* output) {
|
||||||
if (strncmp(output, "!ignore", 7) == 0) {
|
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, TOY_CC_ERROR "Assertion failure: ");
|
||||||
fprintf(stderr, "%s", output);
|
fprintf(stderr, "%s", output);
|
||||||
fprintf(stderr, "\n" TOY_CC_RESET); //default new line
|
fprintf(stderr, "\n" TOY_CC_RESET); //default new line
|
||||||
|
failedAssertions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ void runSourceCustom(const char* source) {
|
|||||||
|
|
||||||
void runSourceFileCustom(const char* fname) {
|
void runSourceFileCustom(const char* fname) {
|
||||||
size_t size = 0; //not used
|
size_t size = 0; //not used
|
||||||
const char* source = Toy_readFile(fname, &size);
|
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||||
runSourceCustom(source);
|
runSourceCustom(source);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,7 @@ int main() {
|
|||||||
Toy_writeCompiler(&compiler, node);
|
Toy_writeCompiler(&compiler, node);
|
||||||
|
|
||||||
//collate
|
//collate
|
||||||
int size = 0;
|
size_t size = 0;
|
||||||
const unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
const unsigned char* bytecode = Toy_collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
//NOTE: suppress print output for testing
|
//NOTE: suppress print output for testing
|
||||||
@@ -108,19 +110,26 @@ int main() {
|
|||||||
//run each file in tests/scripts/
|
//run each file in tests/scripts/
|
||||||
const char* filenames[] = {
|
const char* filenames[] = {
|
||||||
"arithmetic.toy",
|
"arithmetic.toy",
|
||||||
|
"casting-parentheses-bugfix.toy",
|
||||||
"casting.toy",
|
"casting.toy",
|
||||||
"coercions.toy",
|
"coercions.toy",
|
||||||
"comparisons.toy",
|
"comparisons.toy",
|
||||||
"dot-and-matrix.toy",
|
"dot-and-matrix.toy",
|
||||||
"dot-assignments-bugfix.toy",
|
"dot-assignments-bugfix.toy",
|
||||||
"dot-chaining.toy",
|
"dot-chaining.toy",
|
||||||
|
"dot-modulo-bugfix.toy",
|
||||||
"dottify-bugfix.toy",
|
"dottify-bugfix.toy",
|
||||||
|
"function-within-function-bugfix.toy",
|
||||||
"functions.toy",
|
"functions.toy",
|
||||||
|
"group-casting-bugfix.toy",
|
||||||
|
"increment-postfix-bugfix.toy",
|
||||||
"index-arrays.toy",
|
"index-arrays.toy",
|
||||||
"index-assignment-both-bugfix.toy",
|
"index-assignment-both-bugfix.toy",
|
||||||
|
"index-assignment-intermediate-bugfix.toy",
|
||||||
"index-assignment-left-bugfix.toy",
|
"index-assignment-left-bugfix.toy",
|
||||||
"index-dictionaries.toy",
|
"index-dictionaries.toy",
|
||||||
"index-strings.toy",
|
"index-strings.toy",
|
||||||
|
"indexing-in-argument-list-bugfix.toy",
|
||||||
"jumps.toy",
|
"jumps.toy",
|
||||||
"jumps-in-functions.toy",
|
"jumps-in-functions.toy",
|
||||||
"logicals.toy",
|
"logicals.toy",
|
||||||
@@ -132,7 +141,9 @@ int main() {
|
|||||||
"panic-within-functions.toy",
|
"panic-within-functions.toy",
|
||||||
"polyfill-insert.toy",
|
"polyfill-insert.toy",
|
||||||
"polyfill-remove.toy",
|
"polyfill-remove.toy",
|
||||||
|
"short-circuit.toy",
|
||||||
"ternary-expressions.toy",
|
"ternary-expressions.toy",
|
||||||
|
"trailing-comma-bugfix.toy",
|
||||||
"types.toy",
|
"types.toy",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
@@ -153,7 +164,10 @@ int main() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
if (failedAssertions == 0) {
|
||||||
return 0;
|
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedAssertions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -15,10 +15,10 @@ int main() {
|
|||||||
Toy_initLexer(&lexer, source);
|
Toy_initLexer(&lexer, source);
|
||||||
|
|
||||||
//get each token
|
//get each token
|
||||||
Toy_Token print = Toy_scanLexer(&lexer);
|
Toy_Token print = Toy_private_scanLexer(&lexer);
|
||||||
Toy_Token null = Toy_scanLexer(&lexer);
|
Toy_Token null = Toy_private_scanLexer(&lexer);
|
||||||
Toy_Token semi = Toy_scanLexer(&lexer);
|
Toy_Token semi = Toy_private_scanLexer(&lexer);
|
||||||
Toy_Token eof = Toy_scanLexer(&lexer);
|
Toy_Token eof = Toy_private_scanLexer(&lexer);
|
||||||
|
|
||||||
//test each token is correct
|
//test each token is correct
|
||||||
if (strncmp(print.lexeme, "print", print.length)) {
|
if (strncmp(print.lexeme, "print", print.length)) {
|
||||||
|
|||||||
+15
-66
@@ -12,10 +12,13 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../repl/repl_tools.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_runner.h"
|
|
||||||
#include "../repl/lib_standard.h"
|
#include "../repl/lib_standard.h"
|
||||||
|
#include "../repl/lib_random.h"
|
||||||
|
#include "../repl/lib_runner.h"
|
||||||
|
#include "../repl/lib_math.h"
|
||||||
|
|
||||||
//supress the print output
|
//supress the print output
|
||||||
static void noPrintFn(const char* output) {
|
static void noPrintFn(const char* output) {
|
||||||
@@ -45,30 +48,15 @@ void runBinaryWithLibrary(const unsigned char* tb, size_t size, const char* libr
|
|||||||
Toy_setInterpreterError(&interpreter, errorWrapper);
|
Toy_setInterpreterError(&interpreter, errorWrapper);
|
||||||
|
|
||||||
//inject the standard libraries into this interpreter
|
//inject the standard libraries into this interpreter
|
||||||
|
if (hook != Toy_hookStandard) {
|
||||||
|
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||||
|
}
|
||||||
Toy_injectNativeHook(&interpreter, library, hook);
|
Toy_injectNativeHook(&interpreter, library, hook);
|
||||||
|
|
||||||
Toy_runInterpreter(&interpreter, tb, size);
|
Toy_runInterpreter(&interpreter, tb, size);
|
||||||
Toy_freeInterpreter(&interpreter);
|
Toy_freeInterpreter(&interpreter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runBinaryQuietly(const unsigned char* tb, size_t size) {
|
|
||||||
Toy_Interpreter interpreter;
|
|
||||||
Toy_initInterpreter(&interpreter);
|
|
||||||
|
|
||||||
//NOTE: supress print output for testing
|
|
||||||
Toy_setInterpreterPrint(&interpreter, noPrintFn);
|
|
||||||
Toy_setInterpreterAssert(&interpreter, assertWrapper);
|
|
||||||
Toy_setInterpreterError(&interpreter, errorWrapper);
|
|
||||||
|
|
||||||
//inject the libs
|
|
||||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
|
||||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
|
||||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
|
||||||
|
|
||||||
Toy_runInterpreter(&interpreter, tb, size);
|
|
||||||
Toy_freeInterpreter(&interpreter);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct Payload {
|
typedef struct Payload {
|
||||||
char* fname;
|
char* fname;
|
||||||
char* libname;
|
char* libname;
|
||||||
@@ -77,23 +65,19 @@ typedef struct Payload {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
//setup the runner filesystem (hacky)
|
//setup the runner filesystem (hacky)
|
||||||
Toy_initDriveDictionary();
|
Toy_initDriveSystem();
|
||||||
|
|
||||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
Toy_setDrivePath("scripts", "scripts");
|
||||||
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
|
||||||
|
|
||||||
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
|
|
||||||
|
|
||||||
Toy_freeLiteral(driveLiteral);
|
|
||||||
Toy_freeLiteral(pathLiteral);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
//run each file in test/scripts
|
//run each file in test/scripts
|
||||||
Payload payloads[] = {
|
Payload payloads[] = {
|
||||||
{"interactions.toy", "standard", Toy_hookStandard}, //interactions needs standard
|
{"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},
|
{"standard.toy", "standard", Toy_hookStandard},
|
||||||
{"runner.toy", "runner", Toy_hookRunner},
|
{"runner.toy", "runner", Toy_hookRunner},
|
||||||
|
{"random.toy", "random", Toy_hookRandom},
|
||||||
|
{"math.toy", "math", Toy_hookMath},
|
||||||
{NULL, NULL, NULL}
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +89,7 @@ int main() {
|
|||||||
|
|
||||||
//compile the source
|
//compile the source
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const char* source = Toy_readFile(fname, &size);
|
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||||
if (!source) {
|
if (!source) {
|
||||||
printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname);
|
printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname);
|
||||||
failedAsserts++;
|
failedAsserts++;
|
||||||
@@ -125,43 +109,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
//run whatever, testing stuff together to check for memory leaks
|
|
||||||
char* whatever[] = {
|
|
||||||
"random-stuff.toy",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; whatever[i]; i++) {
|
|
||||||
printf("Running %s\n", whatever[i]);
|
|
||||||
|
|
||||||
char fname[128];
|
|
||||||
snprintf(fname, 128, "scripts/lib/%s", whatever[i]);
|
|
||||||
|
|
||||||
//compile the source
|
|
||||||
size_t size = 0;
|
|
||||||
const char* source = Toy_readFile(fname, &size);
|
|
||||||
if (!source) {
|
|
||||||
printf(TOY_CC_ERROR "Failed to load file: %s\n" TOY_CC_RESET, fname);
|
|
||||||
failedAsserts++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned char* tb = Toy_compileString(source, &size);
|
|
||||||
free((void*)source);
|
|
||||||
|
|
||||||
if (!tb) {
|
|
||||||
printf(TOY_CC_ERROR "Failed to compile file: %s\n" TOY_CC_RESET, fname);
|
|
||||||
failedAsserts++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
runBinaryQuietly(tb, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//lib cleanup
|
//lib cleanup
|
||||||
Toy_freeDriveDictionary();
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
if (!failedAsserts) {
|
if (!failedAsserts) {
|
||||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const unsigned char* compileStringCustom(const char* source, size_t* size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//get the bytecode dump
|
//get the bytecode dump
|
||||||
const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
Toy_freeCompiler(&compiler);
|
Toy_freeCompiler(&compiler);
|
||||||
@@ -84,7 +84,7 @@ void runSourceCustom(const char* source) {
|
|||||||
|
|
||||||
void runSourceFileCustom(const char* fname) {
|
void runSourceFileCustom(const char* fname) {
|
||||||
size_t size = 0; //not used
|
size_t size = 0; //not used
|
||||||
const char* source = Toy_readFile(fname, &size);
|
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||||
runSourceCustom(source);
|
runSourceCustom(source);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,7 @@ int main() {
|
|||||||
"declare-types-array.toy",
|
"declare-types-array.toy",
|
||||||
"declare-types-dictionary-key.toy",
|
"declare-types-dictionary-key.toy",
|
||||||
"declare-types-dictionary-value.toy",
|
"declare-types-dictionary-value.toy",
|
||||||
|
"index-access-bugfix.toy",
|
||||||
"index-arrays-non-integer.toy",
|
"index-arrays-non-integer.toy",
|
||||||
"string-concat.toy",
|
"string-concat.toy",
|
||||||
"unary-inverted-nothing.toy",
|
"unary-inverted-nothing.toy",
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ static int consume(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
|||||||
int main() {
|
int main() {
|
||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const char* source = Toy_readFile("scripts/opaque-data-type.toy", &size);
|
const char* source = (const char*)Toy_readFile("scripts/opaque-data-type.toy", &size);
|
||||||
const unsigned char* tb = Toy_compileString(source, &size);
|
const unsigned char* tb = Toy_compileString(source, &size);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -58,7 +58,7 @@ int main() {
|
|||||||
{
|
{
|
||||||
//get the source file
|
//get the source file
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const char* source = Toy_readFile("scripts/parser_sample_code.toy", &size);
|
const char* source = (const char*)Toy_readFile("scripts/parser_sample_code.toy", &size);
|
||||||
|
|
||||||
//test parsing a chunk of junk (valgrind will find leaks)
|
//test parsing a chunk of junk (valgrind will find leaks)
|
||||||
Toy_Lexer lexer;
|
Toy_Lexer lexer;
|
||||||
|
|||||||
@@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
* Project: https://github.com/likle/cargs
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <cargs.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define CAG_OPTION_PRINT_DISTANCE 4
|
||||||
|
#define CAG_OPTION_PRINT_MIN_INDENTION 20
|
||||||
|
|
||||||
|
static void cag_option_print_value(const cag_option *option,
|
||||||
|
size_t *accessor_length, FILE *destination) {
|
||||||
|
if (option->value_name != NULL) {
|
||||||
|
*accessor_length += fprintf(destination, "=%s", option->value_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cag_option_print_letters(const cag_option *option, bool *first,
|
||||||
|
size_t *accessor_length, FILE *destination) {
|
||||||
|
const char *access_letter;
|
||||||
|
access_letter = option->access_letters;
|
||||||
|
if (access_letter != NULL) {
|
||||||
|
while (*access_letter) {
|
||||||
|
if (*first) {
|
||||||
|
*accessor_length += fprintf(destination, "-%c", *access_letter);
|
||||||
|
*first = false;
|
||||||
|
} else {
|
||||||
|
*accessor_length += fprintf(destination, ", -%c",
|
||||||
|
*access_letter);
|
||||||
|
}
|
||||||
|
++access_letter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cag_option_print_name(const cag_option *option, bool *first,
|
||||||
|
size_t *accessor_length, FILE *destination) {
|
||||||
|
if (option->access_name != NULL) {
|
||||||
|
if (*first) {
|
||||||
|
*accessor_length += fprintf(destination, "--%s",
|
||||||
|
option->access_name);
|
||||||
|
} else {
|
||||||
|
*accessor_length += fprintf(destination, ", --%s",
|
||||||
|
option->access_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t cag_option_get_print_indention(const cag_option *options,
|
||||||
|
size_t option_count) {
|
||||||
|
size_t option_index, indention, result;
|
||||||
|
const cag_option *option;
|
||||||
|
|
||||||
|
result = CAG_OPTION_PRINT_MIN_INDENTION;
|
||||||
|
|
||||||
|
for (option_index = 0; option_index < option_count; ++option_index) {
|
||||||
|
indention = CAG_OPTION_PRINT_DISTANCE;
|
||||||
|
option = &options[option_index];
|
||||||
|
if (option->access_letters != NULL && *option->access_letters) {
|
||||||
|
indention += strlen(option->access_letters) * 4 - 2;
|
||||||
|
if (option->access_name != NULL) {
|
||||||
|
indention += strlen(option->access_name) + 4;
|
||||||
|
}
|
||||||
|
} else if (option->access_name != NULL) {
|
||||||
|
indention += strlen(option->access_name) + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option->value_name != NULL) {
|
||||||
|
indention += strlen(option->value_name) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indention > result) {
|
||||||
|
result = indention;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cag_option_print(const cag_option *options, size_t option_count,
|
||||||
|
FILE *destination) {
|
||||||
|
size_t option_index, indention, i, accessor_length;
|
||||||
|
const cag_option *option;
|
||||||
|
bool first;
|
||||||
|
|
||||||
|
indention = cag_option_get_print_indention(options, option_count);
|
||||||
|
|
||||||
|
for (option_index = 0; option_index < option_count; ++option_index) {
|
||||||
|
option = &options[option_index];
|
||||||
|
accessor_length = 0;
|
||||||
|
first = true;
|
||||||
|
|
||||||
|
fputs(" ", destination);
|
||||||
|
|
||||||
|
cag_option_print_letters(option, &first, &accessor_length, destination);
|
||||||
|
cag_option_print_name(option, &first, &accessor_length, destination);
|
||||||
|
cag_option_print_value(option, &accessor_length, destination);
|
||||||
|
|
||||||
|
for (i = accessor_length; i < indention; ++i) {
|
||||||
|
fputs(" ", destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(" ", destination);
|
||||||
|
fputs(option->description, destination);
|
||||||
|
|
||||||
|
fprintf(destination, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cag_option_prepare(cag_option_context *context, const cag_option *options,
|
||||||
|
size_t option_count, int argc, char **argv) {
|
||||||
|
// This just initialized the values to the beginning of all the arguments.
|
||||||
|
context->options = options;
|
||||||
|
context->option_count = option_count;
|
||||||
|
context->argc = argc;
|
||||||
|
context->argv = argv;
|
||||||
|
context->index = 1;
|
||||||
|
context->inner_index = 0;
|
||||||
|
context->forced_end = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const cag_option* cag_option_find_by_name(cag_option_context *context,
|
||||||
|
char *name, size_t name_size) {
|
||||||
|
const cag_option *option;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
// We loop over all the available options and stop as soon as we have found
|
||||||
|
// one. We don't use any hash map table, since there won't be that many
|
||||||
|
// arguments anyway.
|
||||||
|
for (i = 0; i < context->option_count; ++i) {
|
||||||
|
option = &context->options[i];
|
||||||
|
|
||||||
|
// The option might not have an item name, we can just skip those.
|
||||||
|
if (option->access_name == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to compare the name of the access name. We can use the name_size or
|
||||||
|
// this comparison, since we are guaranteed to have null-terminated access
|
||||||
|
// names.
|
||||||
|
if (strncmp(option->access_name, name, name_size) == 0) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const cag_option* cag_option_find_by_letter(cag_option_context *context,
|
||||||
|
char letter) {
|
||||||
|
const cag_option *option;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
// We loop over all the available options and stop as soon as we have found
|
||||||
|
// one. We don't use any look up table, since there won't be that many
|
||||||
|
// arguments anyway.
|
||||||
|
for (i = 0; i < context->option_count; ++i) {
|
||||||
|
option = &context->options[i];
|
||||||
|
|
||||||
|
// If this option doesn't have any access letters we will skip them.
|
||||||
|
if (option->access_letters == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify whether this option has the access letter in it's access letter
|
||||||
|
// string. If it does, then this is our option.
|
||||||
|
if (strchr(option->access_letters, letter) != NULL) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cag_option_parse_value(cag_option_context *context,
|
||||||
|
const cag_option *option, char **c) {
|
||||||
|
// And now let's check whether this option is supposed to have a value, which
|
||||||
|
// is the case if there is a value name set. The value can be either submitted
|
||||||
|
// with a '=' sign or a space, which means we would have to jump over to the
|
||||||
|
// next argv index. This is somewhat ugly, but we do it to behave the same as
|
||||||
|
// the other option parsers.
|
||||||
|
if (option->value_name != NULL) {
|
||||||
|
if (**c == '=') {
|
||||||
|
context->value = ++(*c);
|
||||||
|
} else {
|
||||||
|
// If the next index is larger or equal to the argument count, then the
|
||||||
|
// parameter for this option is missing. The user will know about this,
|
||||||
|
// since the value pointer of the context will be NULL because we don't
|
||||||
|
// set it here in that case.
|
||||||
|
if (context->argc > context->index + 1) {
|
||||||
|
// We consider this argv to be the value, no matter what the contents
|
||||||
|
// are.
|
||||||
|
++context->index;
|
||||||
|
*c = context->argv[context->index];
|
||||||
|
context->value = *c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move c to the end of the value, to not confuse the caller about our
|
||||||
|
// position.
|
||||||
|
while (**c) {
|
||||||
|
++(*c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cag_option_parse_access_name(cag_option_context *context, char **c) {
|
||||||
|
const cag_option *option;
|
||||||
|
char *n;
|
||||||
|
|
||||||
|
// Now we need to extract the access name, which is any symbol up to a '=' or
|
||||||
|
// a '\0'.
|
||||||
|
n = *c;
|
||||||
|
while (**c && **c != '=') {
|
||||||
|
++*c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now this will obviously always be true, but we are paranoid. Sometimes. It
|
||||||
|
// doesn't hurt to check.
|
||||||
|
assert(*c >= n);
|
||||||
|
|
||||||
|
// Figure out which option this name belongs to. This might return NULL if the
|
||||||
|
// name is not registered, which means the user supplied an unknown option. In
|
||||||
|
// that case we return true to indicate that we finished with this option. We
|
||||||
|
// have to skip the value parsing since we don't know whether the user thinks
|
||||||
|
// this option has one or not. Since we don't set any identifier specifically,
|
||||||
|
// it will remain '?' within the context.
|
||||||
|
option = cag_option_find_by_name(context, n, (size_t) (*c - n));
|
||||||
|
if (option == NULL) {
|
||||||
|
// Since this option is invalid, we will move on to the next index. There is
|
||||||
|
// nothing we can do about this.
|
||||||
|
++context->index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found an option and now we can specify the identifier within the
|
||||||
|
// context.
|
||||||
|
context->identifier = option->identifier;
|
||||||
|
|
||||||
|
// And now we try to parse the value. This function will also check whether
|
||||||
|
// this option is actually supposed to have a value.
|
||||||
|
cag_option_parse_value(context, option, c);
|
||||||
|
|
||||||
|
// And finally we move on to the next index.
|
||||||
|
++context->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cag_option_parse_access_letter(cag_option_context *context,
|
||||||
|
char **c) {
|
||||||
|
const cag_option *option;
|
||||||
|
char *n = *c;
|
||||||
|
char *v;
|
||||||
|
|
||||||
|
// Figure out which option this letter belongs to. This might return NULL if
|
||||||
|
// the letter is not registered, which means the user supplied an unknown
|
||||||
|
// option. In that case we return true to indicate that we finished with this
|
||||||
|
// option. We have to skip the value parsing since we don't know whether the
|
||||||
|
// user thinks this option has one or not. Since we don't set any identifier
|
||||||
|
// specifically, it will remain '?' within the context.
|
||||||
|
option = cag_option_find_by_letter(context, n[context->inner_index]);
|
||||||
|
if (option == NULL) {
|
||||||
|
++context->index;
|
||||||
|
context->inner_index = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found an option and now we can specify the identifier within the
|
||||||
|
// context.
|
||||||
|
context->identifier = option->identifier;
|
||||||
|
|
||||||
|
// And now we try to parse the value. This function will also check whether
|
||||||
|
// this option is actually supposed to have a value.
|
||||||
|
v = &n[++context->inner_index];
|
||||||
|
cag_option_parse_value(context, option, &v);
|
||||||
|
|
||||||
|
// Check whether we reached the end of this option argument.
|
||||||
|
if (*v == '\0') {
|
||||||
|
++context->index;
|
||||||
|
context->inner_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cag_option_shift(cag_option_context *context, int start, int option,
|
||||||
|
int end) {
|
||||||
|
char *tmp;
|
||||||
|
int a_index, shift_index, shift_count, left_index, right_index;
|
||||||
|
|
||||||
|
shift_count = option - start;
|
||||||
|
|
||||||
|
// There is no shift is required if the start and the option have the same
|
||||||
|
// index.
|
||||||
|
if (shift_count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lets loop through the option strings first, which we will move towards the
|
||||||
|
// beginning.
|
||||||
|
for (a_index = option; a_index < end; ++a_index) {
|
||||||
|
// First remember the current option value, because we will have to save
|
||||||
|
// that later at the beginning.
|
||||||
|
tmp = context->argv[a_index];
|
||||||
|
|
||||||
|
// Let's loop over all option values and shift them one towards the end.
|
||||||
|
// This will override the option value we just stored temporarily.
|
||||||
|
for (shift_index = 0; shift_index < shift_count; ++shift_index) {
|
||||||
|
left_index = a_index - shift_index;
|
||||||
|
right_index = a_index - shift_index - 1;
|
||||||
|
context->argv[left_index] = context->argv[right_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now restore the saved option value at the beginning.
|
||||||
|
context->argv[a_index - shift_count] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The new index will be before all non-option values, in such a way that they
|
||||||
|
// all will be moved again in the next fetch call.
|
||||||
|
context->index = end - shift_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cag_option_is_argument_string(const char *c) {
|
||||||
|
return *c == '-' && *(c + 1) != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cag_option_find_next(cag_option_context *context) {
|
||||||
|
int next_index, next_option_index;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
// Prepare to search the next option at the next index.
|
||||||
|
next_index = context->index;
|
||||||
|
next_option_index = next_index;
|
||||||
|
|
||||||
|
// Grab a pointer to the string and verify that it is not the end. If it is
|
||||||
|
// the end, we have to return false to indicate that we finished.
|
||||||
|
c = context->argv[next_option_index];
|
||||||
|
if (context->forced_end || c == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether it is a '-'. We need to find the next option - and an option
|
||||||
|
// always starts with a '-'. If there is a string "-\0", we don't consider it
|
||||||
|
// as an option neither.
|
||||||
|
while (!cag_option_is_argument_string(c)) {
|
||||||
|
c = context->argv[++next_option_index];
|
||||||
|
if (c == NULL) {
|
||||||
|
// We reached the end and did not find any argument anymore. Let's tell
|
||||||
|
// our caller that we reached the end.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicate that we found an option which can be processed. The index of the
|
||||||
|
// next option will be returned.
|
||||||
|
return next_option_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cag_option_fetch(cag_option_context *context) {
|
||||||
|
char *c;
|
||||||
|
int old_index, new_index;
|
||||||
|
|
||||||
|
// Reset our identifier to a question mark, which indicates an "unknown"
|
||||||
|
// option. The value is set to NULL, to make sure we are not carrying the
|
||||||
|
// parameter from the previous option to this one.
|
||||||
|
context->identifier = '?';
|
||||||
|
context->value = NULL;
|
||||||
|
|
||||||
|
// Check whether there are any options left to parse and remember the old
|
||||||
|
// index as well as the new index. In the end we will move the option junk to
|
||||||
|
// the beginning, so that non option arguments can be read.
|
||||||
|
old_index = context->index;
|
||||||
|
new_index = cag_option_find_next(context);
|
||||||
|
if (new_index >= 0) {
|
||||||
|
context->index = new_index;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab a pointer to the beginning of the option. At this point, the next
|
||||||
|
// character must be a '-', since if it was not the prepare function would
|
||||||
|
// have returned false. We will skip that symbol and proceed.
|
||||||
|
c = context->argv[context->index];
|
||||||
|
assert(*c == '-');
|
||||||
|
++c;
|
||||||
|
|
||||||
|
// Check whether this is a long option, starting with a double "--".
|
||||||
|
if (*c == '-') {
|
||||||
|
++c;
|
||||||
|
|
||||||
|
// This might be a double "--" which indicates the end of options. If this
|
||||||
|
// is the case, we will not move to the next index. That ensures that
|
||||||
|
// another call to the fetch function will not skip the "--".
|
||||||
|
if (*c == '\0') {
|
||||||
|
context->forced_end = true;
|
||||||
|
} else {
|
||||||
|
// We parse now the access name. All information about it will be written
|
||||||
|
// to the context.
|
||||||
|
cag_option_parse_access_name(context, &c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is no long option, so we can just parse an access letter.
|
||||||
|
cag_option_parse_access_letter(context, &c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the items so that the options come first followed by non-option
|
||||||
|
// arguments.
|
||||||
|
cag_option_shift(context, old_index, new_index, context->index);
|
||||||
|
|
||||||
|
return context->forced_end == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char cag_option_get(const cag_option_context *context) {
|
||||||
|
// We just return the identifier here.
|
||||||
|
return context->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cag_option_get_value(const cag_option_context *context) {
|
||||||
|
// We just return the internal value pointer of the context.
|
||||||
|
return context->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cag_option_get_index(const cag_option_context *context) {
|
||||||
|
// Either we point to a value item,
|
||||||
|
return context->index;
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Project: https://github.com/likle/cargs
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CARGS_H_
|
||||||
|
#define CARGS_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple alternative cross-platform implementation of getopt, which
|
||||||
|
* is used to parse argument strings submitted to the executable (argc and argv
|
||||||
|
* which are received in the main function).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAG_LIBRARY_H
|
||||||
|
#define CAG_LIBRARY_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#define CAG_EXPORT __declspec(dllexport)
|
||||||
|
#define CAG_IMPORT __declspec(dllimport)
|
||||||
|
#elif __GNUC__ >= 4
|
||||||
|
#define CAG_EXPORT __attribute__((visibility("default")))
|
||||||
|
#define CAG_IMPORT __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define CAG_EXPORT
|
||||||
|
#define CAG_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CAG_SHARED)
|
||||||
|
#if defined(CAG_EXPORTS)
|
||||||
|
#define CAG_PUBLIC CAG_EXPORT
|
||||||
|
#else
|
||||||
|
#define CAG_PUBLIC CAG_IMPORT
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define CAG_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An option is used to describe a flag/argument option submitted when the
|
||||||
|
* program is run.
|
||||||
|
*/
|
||||||
|
typedef struct cag_option {
|
||||||
|
const char identifier;
|
||||||
|
const char *access_letters;
|
||||||
|
const char *access_name;
|
||||||
|
const char *value_name;
|
||||||
|
const char *description;
|
||||||
|
} cag_option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context is used to iterate over all options provided. It stores the parsing
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
typedef struct cag_option_context {
|
||||||
|
const struct cag_option *options;
|
||||||
|
size_t option_count;
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
int index;
|
||||||
|
int inner_index;
|
||||||
|
bool forced_end;
|
||||||
|
char identifier;
|
||||||
|
char *value;
|
||||||
|
} cag_option_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is just a small macro which calculates the size of an array.
|
||||||
|
*/
|
||||||
|
#define CAG_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints all options to the terminal.
|
||||||
|
*
|
||||||
|
* This function prints all options to the terminal. This can be used to
|
||||||
|
* generate the output for a "--help" option.
|
||||||
|
*
|
||||||
|
* @param options The options which will be printed.
|
||||||
|
* @param option_count The option count which will be printed.
|
||||||
|
* @param destination The destination where the output will be printed.
|
||||||
|
*/
|
||||||
|
CAG_PUBLIC void cag_option_print(const cag_option *options, size_t option_count,
|
||||||
|
FILE *destination);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepare argument options context for parsing.
|
||||||
|
*
|
||||||
|
* This function prepares the context for iteration and initializes the context
|
||||||
|
* with the supplied options and arguments. After the context has been prepared,
|
||||||
|
* it can be used to fetch arguments from it.
|
||||||
|
*
|
||||||
|
* @param context The context which will be initialized.
|
||||||
|
* @param options The registered options which are available for the program.
|
||||||
|
* @param option_count The amount of options which are available for the
|
||||||
|
* program.
|
||||||
|
* @param argc The amount of arguments the user supplied in the main function.
|
||||||
|
* @param argv A pointer to the arguments of the main function.
|
||||||
|
*/
|
||||||
|
CAG_PUBLIC void cag_option_prepare(cag_option_context *context,
|
||||||
|
const cag_option *options, size_t option_count, int argc, char **argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetches an option from the argument list.
|
||||||
|
*
|
||||||
|
* This function fetches a single option from the argument list. The context
|
||||||
|
* will be moved to that item. Information can be extracted from the context
|
||||||
|
* after the item has been fetched.
|
||||||
|
* The arguments will be re-ordered, which means that non-option arguments will
|
||||||
|
* be moved to the end of the argument list. After all options have been
|
||||||
|
* fetched, all non-option arguments will be positioned after the index of
|
||||||
|
* the context.
|
||||||
|
*
|
||||||
|
* @param context The context from which we will fetch the option.
|
||||||
|
* @return Returns true if there was another option or false if the end is
|
||||||
|
* reached.
|
||||||
|
*/
|
||||||
|
CAG_PUBLIC bool cag_option_fetch(cag_option_context *context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the identifier of the option.
|
||||||
|
*
|
||||||
|
* This function gets the identifier of the option, which should be unique to
|
||||||
|
* this option and can be used to determine what kind of option this is.
|
||||||
|
*
|
||||||
|
* @param context The context from which the option was fetched.
|
||||||
|
* @return Returns the identifier of the option.
|
||||||
|
*/
|
||||||
|
CAG_PUBLIC char cag_option_get(const cag_option_context *context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the value from the option.
|
||||||
|
*
|
||||||
|
* This function gets the value from the option, if any. If the option does not
|
||||||
|
* contain a value, this function will return NULL.
|
||||||
|
*
|
||||||
|
* @param context The context from which the option was fetched.
|
||||||
|
* @return Returns a pointer to the value or NULL if there is no value.
|
||||||
|
*/
|
||||||
|
CAG_PUBLIC const char* cag_option_get_value(const cag_option_context *context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current index of the context.
|
||||||
|
*
|
||||||
|
* This function gets the index within the argv arguments of the context. The
|
||||||
|
* context always points to the next item which it will inspect. This is
|
||||||
|
* particularly useful to inspect the original argument array, or to get
|
||||||
|
* non-option arguments after option fetching has finished.
|
||||||
|
*
|
||||||
|
* @param context The context from which the option was fetched.
|
||||||
|
* @return Returns the current index of the context.
|
||||||
|
*/
|
||||||
|
CAG_PUBLIC int cag_option_get_index(const cag_option_context *context);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CARGS_H_ */
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user