Compare commits
194 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 57af5a6d59 | |||
| 0737b2a483 | |||
| eae96d6403 | |||
| b55b8e879e | |||
| 1ed114b80d | |||
| eb8e522bf2 | |||
| 16b71ba6f4 | |||
| 9725f3c6a3 | |||
| 8653a2663f | |||
| ab2cd5dc93 | |||
| 724804a78a | |||
| 77a128e0f7 | |||
| 5343e1054d | |||
| 3930ec0477 | |||
| 996744d7ec | |||
| c00b32017b | |||
| 457014d577 | |||
| be4cbf1ad6 | |||
| aeb008c684 | |||
| 53012dbce1 | |||
| 4fe57f9562 | |||
| 3ba2e420ea | |||
| c81a139c97 | |||
| 66ea684a90 | |||
| a26a6a56d0 | |||
| ee226ea426 | |||
| 76a0290290 | |||
| 85dc24c4a6 | |||
| 1804e380a0 | |||
| 7567f4f3b2 | |||
| e8160eb9df | |||
| 276648630e | |||
| e946a5f071 | |||
| 932401c26d | |||
| fc67d6a18b | |||
| 60908c8bf3 | |||
| b8e3324acb | |||
| 0fc8183799 | |||
| 9a6aa8d15e | |||
| 0b9051cab2 | |||
| 0d7e4db661 | |||
| d5c833b344 | |||
| c875ae7a0e | |||
| c0ec5ef28e | |||
| be91de9535 | |||
| 3088c4fe6d | |||
| 9bd0cd23e7 | |||
| 29f8a698b4 | |||
| 41d274177a | |||
| 7ea249f723 | |||
| 3949be3706 | |||
| 0e932f24cc | |||
| 60225b733b | |||
| 15f99495a1 | |||
| 7b26527e95 | |||
| 386201b6e9 | |||
| 8d278077b1 | |||
| 57c16d2ede | |||
| 2f1613e306 | |||
| 2776c87026 | |||
| 8cc33f5fbb | |||
| 90f91a234a | |||
| fcd1cdf70b | |||
| e265038547 | |||
| a357e0b0d4 | |||
| 2c5e3c6aa5 | |||
| aeda0a0d94 | |||
| ebbcba1b16 | |||
| 4bce10803e | |||
| daee91904d | |||
| c397384766 | |||
| 667efc2feb | |||
| f4469fc53d | |||
| c86c5800a7 | |||
| e4d843ad3a | |||
| 6667bce3d7 | |||
| 50b07da188 | |||
| 708db75760 | |||
| b632ce77ba | |||
| b2c3dd894c | |||
| 9b21bfb53b | |||
| d3adac8a59 | |||
| 5183037a99 | |||
| 2e2bee4fa3 | |||
| 047ccc5f16 | |||
| 59b0d15915 | |||
| 6d961cea78 | |||
| 33f360c9cf | |||
| d3516b4fc9 | |||
| df85d30553 | |||
| d57fca474f | |||
| 69f5151310 | |||
| c47ee68b3f | |||
| 2c06c5e38b | |||
| e6e24ca19f | |||
| 390f60e0ce | |||
| 580e407b05 | |||
| 26bd476c2c | |||
| b8b67fce3d | |||
| 68ed39fc45 | |||
| aeecfabbbc | |||
| 51740e2b9e | |||
| 402abb647c | |||
| 3aa7bf1877 | |||
| ae1dc5841e | |||
| 4b60d65203 | |||
| 3498baad9b | |||
| 0649a141dd | |||
| 65e5905011 | |||
| bf64275aa9 | |||
| 08b400debf | |||
| 9ad2a6cf2e | |||
| 8009f410a4 | |||
| 584fb115b6 | |||
| 70698a4a1a | |||
| eb26d23363 |
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: krgamestudios
|
||||
ko_fi: krgamestudios
|
||||
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
---
|
||||
|
||||
## Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
## To Reproduce
|
||||
|
||||
Steps to reproduce the behaviour:
|
||||
|
||||
1. run `git pull` on the repository
|
||||
2. run `make rebuild` on the code
|
||||
3. ...
|
||||
|
||||
You can include some screenshots here if you'd like!
|
||||
|
||||
## Versioning
|
||||
|
||||
- OS: [for example MacOS, Windows, iOS, Android]
|
||||
- Version: [What version of Toy was this running?]
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
### Describe the feature you’d like
|
||||
|
||||
A clear and concise description of what you’d like to be able to do with Toy.
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
A clear and concise description of any alternative solutions or workarounds you've considered.
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the feature request here.
|
||||
@@ -7,13 +7,28 @@ on:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install valgrind
|
||||
run: sudo apt install valgrind
|
||||
- name: make test
|
||||
- name: make test (valgrind)
|
||||
run: make test
|
||||
|
||||
test-sanitized:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: make test (sanitized)
|
||||
run: make test-sanitized
|
||||
|
||||
test-mingw32:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: make test (mingw32)
|
||||
run: make test
|
||||
|
||||
+6
-3
@@ -1,6 +1,4 @@
|
||||
#Editor generated files
|
||||
*.sln
|
||||
*.vcproj
|
||||
*.suo
|
||||
*.ncb
|
||||
*.user
|
||||
@@ -13,18 +11,23 @@ Out/
|
||||
release/
|
||||
debug/
|
||||
out/
|
||||
bin/
|
||||
.cache/
|
||||
.vs/
|
||||
|
||||
#Project generated files
|
||||
*.db
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.dll
|
||||
*.exe
|
||||
*.meta
|
||||
*.log
|
||||
out
|
||||
*.out
|
||||
*.stackdump
|
||||
*.tb
|
||||
*.filters
|
||||
|
||||
#Shell files
|
||||
*.bat
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# License
|
||||
|
||||
Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios
|
||||
Copyright (c) 2020-2023 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.
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
# Toy
|
||||
|
||||
This is the Toy programming language interpreter, written in C.
|
||||
The Toy programming language is an imperative bytecode-intermediate embedded scripting language. It isn't intended to operate on its own, but rather as part of another program, the "host". This process is intended to allow a decent amount of easy customisation by the host's end user, by exposing logic in script files. Alternatively, binary files in a custom format can be used as well.
|
||||
|
||||
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
||||
The host will provide all of the extensions needed on a case-by-case basis. Script files have the `.toy` file extension, while binary files have the `.tb` file extension.
|
||||
|
||||
This is the Toy programming language interpreter, written in C.
|
||||
|
||||
# Nifty Features
|
||||
|
||||
@@ -14,18 +16,18 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
|
||||
* Bytecode intermediate compilation
|
||||
* Optional, but robust type system (including `opaque` for arbitrary data)
|
||||
* Functions and types are first-class citizens
|
||||
* `import` and `export` variables from the host program
|
||||
* Import native libraries from the host
|
||||
* Fancy slice notation for strings, arrays and dictionaries
|
||||
* Can re-direct output, error and assertion failure messages
|
||||
* Open source under the zlib license
|
||||
|
||||
## Building
|
||||
|
||||
For Windows, Linux and MacOS, simply run `make` in the root directory.
|
||||
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
|
||||
|
||||
Note: For Linux, you may need to `cd` into the `out` directory before running.
|
||||
For Windows(MSVC), Visual Studio project files are included.
|
||||
|
||||
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
|
||||
Note: MacOS and Windows(MSVC) are not officially supported, but we'll do our best!
|
||||
|
||||
## Tools
|
||||
|
||||
@@ -38,7 +40,6 @@ Run `make install-tools` to install a number of tools, including:
|
||||
```
|
||||
import standard; //for a bunch of utility functions
|
||||
|
||||
|
||||
print "Hello world"; //"print" is a keyword
|
||||
|
||||
var msg = "foobar"; //declare a variable like this
|
||||
@@ -62,10 +63,14 @@ var tally = makeCounter();
|
||||
print tally(); //1
|
||||
print tally(); //2
|
||||
print tally(); //3
|
||||
|
||||
export tally; //export this variable to the host program
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
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)).
|
||||
|
||||
# Patrons via Patreon
|
||||
|
||||
* Seth A. Robinson
|
||||
|
||||
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{97F823E5-3AB8-47EF-B142-C15DD7CADF76}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IgnoreImportLibrary>false</IgnoreImportLibrary>
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Link>
|
||||
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</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\$(Configuration)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="repl\lib_about.c" />
|
||||
<ClCompile Include="repl\lib_random.c" />
|
||||
<ClCompile Include="repl\lib_runner.c" />
|
||||
<ClCompile Include="repl\lib_standard.c" />
|
||||
<ClCompile Include="repl\repl_main.c" />
|
||||
<ClCompile Include="repl\repl_tools.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="repl\lib_about.h" />
|
||||
<ClInclude Include="repl\lib_random.h" />
|
||||
<ClInclude Include="repl\lib_runner.h" />
|
||||
<ClInclude Include="repl\lib_standard.h" />
|
||||
<ClInclude Include="repl\repl_tools.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="Toy.vcxproj">
|
||||
<Project>{26360002-cc2a-469a-9b28-ba0c1af41657}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{26360002-CC2A-469A-9B28-BA0C1AF41657}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TOY_EXPORTS;TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;TOY_EXPORTS;TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="source\toy_ast_node.c" />
|
||||
<ClCompile Include="source\toy_builtin.c" />
|
||||
<ClCompile Include="source\toy_common.c" />
|
||||
<ClCompile Include="source\toy_compiler.c" />
|
||||
<ClCompile Include="source\toy_drive_system.c" />
|
||||
<ClCompile Include="source\toy_interpreter.c" />
|
||||
<ClCompile Include="source\toy_keyword_types.c" />
|
||||
<ClCompile Include="source\toy_lexer.c" />
|
||||
<ClCompile Include="source\toy_literal.c" />
|
||||
<ClCompile Include="source\toy_literal_array.c" />
|
||||
<ClCompile Include="source\toy_literal_dictionary.c" />
|
||||
<ClCompile Include="source\toy_memory.c" />
|
||||
<ClCompile Include="source\toy_parser.c" />
|
||||
<ClCompile Include="source\toy_reffunction.c" />
|
||||
<ClCompile Include="source\toy_refstring.c" />
|
||||
<ClCompile Include="source\toy_scope.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="source\toy.h" />
|
||||
<ClInclude Include="source\toy_ast_node.h" />
|
||||
<ClInclude Include="source\toy_builtin.h" />
|
||||
<ClInclude Include="source\toy_common.h" />
|
||||
<ClInclude Include="source\toy_compiler.h" />
|
||||
<ClInclude Include="source\toy_console_colors.h" />
|
||||
<ClInclude Include="source\toy_drive_system.h" />
|
||||
<ClInclude Include="source\toy_interpreter.h" />
|
||||
<ClInclude Include="source\toy_keyword_types.h" />
|
||||
<ClInclude Include="source\toy_lexer.h" />
|
||||
<ClInclude Include="source\toy_literal.h" />
|
||||
<ClInclude Include="source\toy_literal_array.h" />
|
||||
<ClInclude Include="source\toy_literal_dictionary.h" />
|
||||
<ClInclude Include="source\toy_memory.h" />
|
||||
<ClInclude Include="source\toy_opcodes.h" />
|
||||
<ClInclude Include="source\toy_parser.h" />
|
||||
<ClInclude Include="source\toy_reffunction.h" />
|
||||
<ClInclude Include="source\toy_refstring.h" />
|
||||
<ClInclude Include="source\toy_scope.h" />
|
||||
<ClInclude Include="source\toy_token_types.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33213.308
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Toy", "Toy.vcxproj", "{26360002-CC2A-469A-9B28-BA0C1AF41657}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Repl", "Repl.vcxproj", "{97F823E5-3AB8-47EF-B142-C15DD7CADF76}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657} = {26360002-CC2A-469A-9B28-BA0C1AF41657}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.Build.0 = Debug|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.Build.0 = Debug|Win32
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.ActiveCfg = Release|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.Build.0 = Release|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.ActiveCfg = Release|Win32
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.Build.0 = Release|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.Build.0 = Debug|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.Build.0 = Debug|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.ActiveCfg = Release|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.Build.0 = Release|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.ActiveCfg = Release|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7089F1AD-8EC0-4F27-AFD1-5FD43D91AABC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,5 +1,4 @@
|
||||
# Optimisation Options
|
||||
# export CFLAGS+=-O2 -mtune=native -march=native
|
||||
export CFLAGS+=-std=c18 -pedantic -Werror
|
||||
|
||||
export TOY_OUTDIR = out
|
||||
|
||||
@@ -7,10 +6,10 @@ all: $(TOY_OUTDIR) repl
|
||||
|
||||
#repl builds
|
||||
repl: $(TOY_OUTDIR) library
|
||||
$(MAKE) -C repl
|
||||
$(MAKE) -j8 -C repl
|
||||
|
||||
repl-static: $(TOY_OUTDIR) static
|
||||
$(MAKE) -C repl
|
||||
$(MAKE) -j8 -C repl
|
||||
|
||||
repl-release: clean $(TOY_OUTDIR) library-release
|
||||
$(MAKE) -C repl release
|
||||
@@ -25,16 +24,26 @@ library: $(TOY_OUTDIR)
|
||||
static: $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source static
|
||||
|
||||
library-release: $(TOY_OUTDIR)
|
||||
library-release: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source library-release
|
||||
|
||||
static-release: $(TOY_OUTDIR)
|
||||
static-release: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source static-release
|
||||
|
||||
#distribution
|
||||
dist: export CFLAGS+=-O2 -mtune=native -march=native
|
||||
dist: repl-release
|
||||
|
||||
#utils
|
||||
test: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -C test
|
||||
|
||||
test-sanitized: export CFLAGS+=-fsanitize=address,undefined
|
||||
test-sanitized: export LIBS+=-static-libasan
|
||||
test-sanitized: export DISABLE_VALGRIND=true
|
||||
test-sanitized: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -C test
|
||||
|
||||
$(TOY_OUTDIR):
|
||||
mkdir $(TOY_OUTDIR)
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "lib_about.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//the about keys
|
||||
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
|
||||
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
|
||||
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
|
||||
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
|
||||
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
|
||||
|
||||
//the about identifiers
|
||||
Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
|
||||
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
|
||||
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
|
||||
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
|
||||
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
|
||||
|
||||
//the about values
|
||||
Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
|
||||
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
|
||||
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
|
||||
Toy_Literal buildLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(TOY_VERSION_BUILD));
|
||||
Toy_Literal authorLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("Kayne Ruse, KR Game Studios"));
|
||||
|
||||
//store as an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with values
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//set each key/value pair
|
||||
Toy_setLiteralDictionary(dictionary, majorKeyLiteral, majorLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, minorKeyLiteral, minorLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, patchKeyLiteral, patchLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, buildKeyLiteral, buildLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, authorKeyLiteral, authorLiteral);
|
||||
|
||||
//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 anyType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, anyType);
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
//store globally
|
||||
else {
|
||||
//make sure the names aren't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal intType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_INTEGER, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
|
||||
//major
|
||||
Toy_declareScopeVariable(interpreter->scope, majorIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, majorIdentifierLiteral, majorLiteral, false);
|
||||
|
||||
//minor
|
||||
Toy_declareScopeVariable(interpreter->scope, minorIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, minorIdentifierLiteral, minorLiteral, false);
|
||||
|
||||
//patch
|
||||
Toy_declareScopeVariable(interpreter->scope, patchIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, patchIdentifierLiteral, patchLiteral, false);
|
||||
|
||||
//build
|
||||
Toy_declareScopeVariable(interpreter->scope, buildIdentifierLiteral, strType);
|
||||
Toy_setScopeVariable(interpreter->scope, buildIdentifierLiteral, buildLiteral, false);
|
||||
|
||||
//author
|
||||
Toy_declareScopeVariable(interpreter->scope, authorIdentifierLiteral, strType);
|
||||
Toy_setScopeVariable(interpreter->scope, authorIdentifierLiteral, authorLiteral, false);
|
||||
|
||||
Toy_freeLiteral(intType);
|
||||
Toy_freeLiteral(strType);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
#include "lib_random.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
static int hashInt(int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
typedef struct Toy_RandomGenerator {
|
||||
int seed; //mutated with each call
|
||||
} Toy_RandomGenerator;
|
||||
|
||||
//Toy native functions
|
||||
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the seed argument
|
||||
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal seedLiteralIdn = seedLiteral;
|
||||
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
|
||||
Toy_freeLiteral(seedLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(seedLiteral)) {
|
||||
Toy_freeLiteral(seedLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(seedLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
|
||||
Toy_freeLiteral(seedLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//generate the generator object
|
||||
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
|
||||
generator->seed = TOY_AS_INTEGER(seedLiteral);
|
||||
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
|
||||
|
||||
//return and cleanup
|
||||
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
|
||||
|
||||
Toy_freeLiteral(seedLiteral);
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||
|
||||
//generate the new value and package up the return
|
||||
generator->seed = hashInt(generator->seed);
|
||||
|
||||
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
Toy_freeLiteral(resultLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||
|
||||
//clear out the runner object
|
||||
TOY_FREE(Toy_RandomGenerator, generator);
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
const char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"createRandomGenerator", nativeCreateRandomGenerator},
|
||||
{"generateRandomNumber", nativeGenerateRandomNumber},
|
||||
{"freeRandomGenerator", nativeFreeRandomGenerator},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
#define TOY_OPAQUE_TAG_RANDOM 200
|
||||
@@ -0,0 +1,553 @@
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
#include "toy_drive_system.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
#include "repl_tools.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct Toy_Runner {
|
||||
Toy_Interpreter interpreter;
|
||||
const unsigned char* bytecode;
|
||||
size_t size;
|
||||
|
||||
bool dirty;
|
||||
} Toy_Runner;
|
||||
|
||||
//Toy native functions
|
||||
static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to loadScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the file path literal with a handle
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
|
||||
|
||||
if (TOY_IS_NULL(filePathLiteral)) {
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//use raw types - easier
|
||||
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
|
||||
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
|
||||
|
||||
//load and compile the bytecode
|
||||
size_t fileSize = 0;
|
||||
const char* source = (const char*)Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!source) {
|
||||
interpreter->errorOutput("Failed to load source file\n");
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const unsigned char* bytecode = Toy_compileString(source, &fileSize);
|
||||
free((void*)source);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to compile source file\n");
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//build the runner object
|
||||
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
|
||||
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
|
||||
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
|
||||
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
|
||||
runner->interpreter.hooks = interpreter->hooks;
|
||||
runner->interpreter.scope = NULL;
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->bytecode = bytecode;
|
||||
runner->size = fileSize;
|
||||
runner->dirty = false;
|
||||
|
||||
//build the opaque object, and push it to the stack
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
//free the drive path
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to loadScriptBytecode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the argument
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
|
||||
|
||||
if (TOY_IS_NULL(filePathLiteral)) {
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//use raw types - easier
|
||||
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
|
||||
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
|
||||
|
||||
//load the bytecode
|
||||
size_t fileSize = 0;
|
||||
unsigned char* bytecode = (unsigned char*)Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to load bytecode file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//build the runner object
|
||||
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
|
||||
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
|
||||
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
|
||||
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
|
||||
runner->interpreter.hooks = interpreter->hooks;
|
||||
runner->interpreter.scope = NULL;
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->bytecode = bytecode;
|
||||
runner->size = fileSize;
|
||||
runner->dirty = false;
|
||||
|
||||
//build the opaque object, and push it to the stack
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
//free the drive path
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//run
|
||||
if (runner->dirty) {
|
||||
interpreter->errorOutput("Can't re-run a dirty script (try resetting it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* bytecodeCopy = TOY_ALLOCATE(unsigned char, runner->size);
|
||||
memcpy(bytecodeCopy, runner->bytecode, runner->size); //need a COPY of the bytecode, because the interpreter eats it
|
||||
|
||||
Toy_runInterpreter(&runner->interpreter, bytecodeCopy, runner->size);
|
||||
runner->dirty = true;
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to getScriptVar\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal varName = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal varNameIdn = varName;
|
||||
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
|
||||
Toy_freeLiteral(varNameIdn);
|
||||
}
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in getScriptVar\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//dirty check
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't access variable from a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the desired variable
|
||||
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &result);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count < 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to callScriptFn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the rest args
|
||||
Toy_LiteralArray tmp;
|
||||
Toy_initLiteralArray(&tmp);
|
||||
|
||||
while (arguments->count > 2) {
|
||||
Toy_Literal lit = Toy_popLiteralArray(arguments);
|
||||
Toy_pushLiteralArray(&tmp, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
Toy_LiteralArray rest;
|
||||
Toy_initLiteralArray(&rest);
|
||||
|
||||
while (tmp.count > 0) { //correct the order of the rest args
|
||||
Toy_Literal lit = Toy_popLiteralArray(&tmp);
|
||||
Toy_pushLiteralArray(&rest, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
Toy_freeLiteralArray(&tmp);
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal varName = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal varNameIdn = varName;
|
||||
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
|
||||
Toy_freeLiteral(varNameIdn);
|
||||
}
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in callScriptFn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//dirty check
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't access fn from a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the desired variable
|
||||
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
|
||||
Toy_Literal fn = TOY_TO_NULL_LITERAL;
|
||||
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &fn);
|
||||
|
||||
if (!TOY_IS_FUNCTION(fn)) {
|
||||
interpreter->errorOutput("Can't run a non-function literal\n");
|
||||
Toy_freeLiteral(fn);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
}
|
||||
|
||||
//call
|
||||
Toy_LiteralArray resultArray;
|
||||
Toy_initLiteralArray(&resultArray);
|
||||
|
||||
Toy_callLiteralFn(interpreter, fn, &rest, &resultArray);
|
||||
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
if (resultArray.count > 0) {
|
||||
result = Toy_popLiteralArray(&resultArray);
|
||||
}
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteralArray(&resultArray);
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(fn);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to resetScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in resetScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//reset
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't reset a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->dirty = false;
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to freeScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in freeScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//clear out the runner object
|
||||
runner->interpreter.hooks = NULL;
|
||||
Toy_freeInterpreter(&runner->interpreter);
|
||||
TOY_FREE_ARRAY(unsigned char, runner->bytecode, runner->size);
|
||||
|
||||
TOY_FREE(Toy_Runner, runner);
|
||||
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to checkScriptDirty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in checkScriptDirty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//run
|
||||
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(runner->dirty);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
const char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"loadScript", nativeLoadScript},
|
||||
{"loadScriptBytecode", nativeLoadScriptBytecode},
|
||||
{"runScript", nativeRunScript},
|
||||
{"getScriptVar", nativeGetScriptVar},
|
||||
{"callScriptFn", nativeCallScriptFn},
|
||||
{"resetScript", nativeResetScript},
|
||||
{"freeScript", nativeFreeScript},
|
||||
{"checkScriptDirty", nativeCheckScriptDirty},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
#define TOY_OPAQUE_TAG_RUNNER 100
|
||||
|
||||
+2055
-32
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "interpreter.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
|
||||
@@ -1,411 +0,0 @@
|
||||
#include "lib_timer.h"
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
|
||||
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
|
||||
//normallize
|
||||
if (x->tv_usec > 999999) {
|
||||
x->tv_sec += x->tv_usec / 1000000;
|
||||
x->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
if (y->tv_usec > 999999) {
|
||||
y->tv_sec += y->tv_usec / 1000000;
|
||||
y->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
//calc
|
||||
result->tv_sec = x->tv_sec - y->tv_sec;
|
||||
|
||||
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
|
||||
if (result->tv_sec != 0) { //only works far from 0
|
||||
result->tv_usec += 1000000;
|
||||
result->tv_sec--; // borrow
|
||||
}
|
||||
}
|
||||
|
||||
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
|
||||
}
|
||||
|
||||
//god damn it
|
||||
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
|
||||
struct timeval* d = ALLOCATE(struct timeval, 1);
|
||||
|
||||
//I gave up, copied from SO
|
||||
timeval_subtract(d, rhs, lhs);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//callbacks
|
||||
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 0) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the timeinfo from C
|
||||
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
||||
gettimeofday(timeinfo, NULL);
|
||||
|
||||
//wrap in an opaque literal for Toy
|
||||
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||
|
||||
freeLiteral(timeLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the timeinfo from C
|
||||
struct timeval timerStop;
|
||||
gettimeofday(&timerStop, NULL);
|
||||
|
||||
//unwrap the opaque literal
|
||||
Literal timeLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal timeLiteralIdn = timeLiteral;
|
||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
|
||||
freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
|
||||
|
||||
//determine the difference, and wrap it
|
||||
struct timeval* d = diff(timerStart, &timerStop);
|
||||
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
||||
pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||
|
||||
//cleanup
|
||||
freeLiteral(timeLiteral);
|
||||
freeLiteral(diffLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the args
|
||||
Literal microsecondLiteral = popLiteralArray(arguments);
|
||||
Literal secondLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal secondLiteralIdn = secondLiteral;
|
||||
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
|
||||
freeLiteral(secondLiteralIdn);
|
||||
}
|
||||
|
||||
Literal microsecondLiteralIdn = microsecondLiteral;
|
||||
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, µsecondLiteral)) {
|
||||
freeLiteral(microsecondLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
|
||||
freeLiteral(secondLiteral);
|
||||
freeLiteral(microsecondLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) {
|
||||
interpreter->errorOutput("Microseconds out of range in createTimer\n");
|
||||
freeLiteral(secondLiteral);
|
||||
freeLiteral(microsecondLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the timeinfo from toy
|
||||
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
||||
timeinfo->tv_sec = AS_INTEGER(secondLiteral);
|
||||
timeinfo->tv_usec = AS_INTEGER(microsecondLiteral);
|
||||
|
||||
//wrap in an opaque literal for Toy
|
||||
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||
|
||||
freeLiteral(timeLiteral);
|
||||
freeLiteral(secondLiteral);
|
||||
freeLiteral(microsecondLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap the opaque literal
|
||||
Literal timeLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal timeLiteralIdn = timeLiteral;
|
||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
|
||||
freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||
|
||||
//create the result literal
|
||||
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
|
||||
pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
freeLiteral(timeLiteral);
|
||||
freeLiteral(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap the opaque literal
|
||||
Literal timeLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal timeLiteralIdn = timeLiteral;
|
||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
|
||||
freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||
|
||||
//create the result literal
|
||||
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
|
||||
pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
freeLiteral(timeLiteral);
|
||||
freeLiteral(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap the opaque literals
|
||||
Literal rhsLiteral = popLiteralArray(arguments);
|
||||
Literal lhsLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal lhsLiteralIdn = lhsLiteral;
|
||||
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
|
||||
freeLiteral(lhsLiteralIdn);
|
||||
}
|
||||
|
||||
Literal rhsLiteralIdn = rhsLiteral;
|
||||
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
|
||||
freeLiteral(rhsLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
|
||||
freeLiteral(lhsLiteral);
|
||||
freeLiteral(rhsLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
|
||||
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
|
||||
|
||||
//determine the difference, and wrap it
|
||||
struct timeval* d = diff(lhsTimer, rhsTimer);
|
||||
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
||||
pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||
|
||||
//cleanup
|
||||
freeLiteral(lhsLiteral);
|
||||
freeLiteral(rhsLiteral);
|
||||
freeLiteral(diffLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap in an opaque literal
|
||||
Literal timeLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal timeLiteralIdn = timeLiteral;
|
||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
|
||||
freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||
|
||||
//create the string literal
|
||||
Literal resultLiteral = TO_NULL_LITERAL;
|
||||
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
|
||||
resultLiteral = TO_STRING_LITERAL(createRefStringLength(buffer, strlen(buffer)));
|
||||
}
|
||||
else { //normal case
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
|
||||
resultLiteral = TO_STRING_LITERAL(createRefStringLength(buffer, strlen(buffer)));
|
||||
}
|
||||
|
||||
pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||
|
||||
//cleanup
|
||||
freeLiteral(timeLiteral);
|
||||
freeLiteral(resultLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap in an opaque literal
|
||||
Literal timeLiteral = popLiteralArray(arguments);
|
||||
|
||||
Literal timeLiteralIdn = timeLiteral;
|
||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
|
||||
freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||
|
||||
FREE(struct timeval, timer);
|
||||
|
||||
freeLiteral(timeLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
char* name;
|
||||
NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"startTimer", nativeStartTimer},
|
||||
{"_stopTimer", nativeStopTimer},
|
||||
{"createTimer", nativeCreateTimer},
|
||||
{"_getTimerSeconds", nativeGetTimerSeconds},
|
||||
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
|
||||
{"_compareTimer", nativeCompareTimer},
|
||||
{"_timerToString", nativeTimerToString},
|
||||
{"_destroyTimer", nativeDestroyTimer},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
freeLiteral(alias);
|
||||
return false;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||
initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Literal name = TO_STRING_LITERAL(createRefString(natives[i].name));
|
||||
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
freeLiteral(name);
|
||||
freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
||||
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
||||
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
||||
TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
||||
declareScopeVariable(interpreter->scope, alias, type);
|
||||
setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
freeLiteral(dict);
|
||||
freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||
|
||||
+146
-57
@@ -1,36 +1,43 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_about.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_timer.h"
|
||||
#include "lib_random.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "console_colors.h"
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "interpreter.h"
|
||||
#include "toy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void repl() {
|
||||
#define INPUT_BUFFER_SIZE 2048
|
||||
|
||||
void repl(const char* initialInput) {
|
||||
//repl does it's own thing for now
|
||||
bool error = false;
|
||||
|
||||
const int size = 2048;
|
||||
char input[size];
|
||||
memset(input, 0, size);
|
||||
char input[INPUT_BUFFER_SIZE];
|
||||
memset(input, 0, INPUT_BUFFER_SIZE);
|
||||
|
||||
Interpreter interpreter; //persist the interpreter for the scopes
|
||||
initInterpreter(&interpreter);
|
||||
Toy_Interpreter interpreter; //persist the interpreter for the scopes
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
for(;;) {
|
||||
printf("> ");
|
||||
fgets(input, size, stdin);
|
||||
if (!initialInput) {
|
||||
//handle EOF for exits
|
||||
printf("> ");
|
||||
if (!fgets(input, INPUT_BUFFER_SIZE, stdin)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//escape the repl (length of 5 to accomodate the newline)
|
||||
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
|
||||
@@ -38,104 +45,186 @@ void repl() {
|
||||
}
|
||||
|
||||
//setup this iteration
|
||||
Lexer lexer;
|
||||
Parser parser;
|
||||
Compiler compiler;
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
|
||||
initLexer(&lexer, input);
|
||||
initParser(&parser, &lexer);
|
||||
initCompiler(&compiler);
|
||||
Toy_initLexer(&lexer, initialInput ? initialInput : input);
|
||||
Toy_private_setComments(&lexer, initialInput != NULL); //BUGFIX: disable comments here
|
||||
Toy_initParser(&parser, &lexer);
|
||||
Toy_initCompiler(&compiler);
|
||||
|
||||
//run this iteration
|
||||
ASTNode* node = scanParser(&parser);
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//pack up and restart
|
||||
if (node->type == AST_NODE_ERROR) {
|
||||
printf(ERROR "error node detected\n" RESET);
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
|
||||
}
|
||||
error = true;
|
||||
freeASTNode(node);
|
||||
Toy_freeASTNode(node);
|
||||
break;
|
||||
}
|
||||
|
||||
writeCompiler(&compiler, node);
|
||||
freeASTNode(node);
|
||||
node = scanParser(&parser);
|
||||
Toy_writeCompiler(&compiler, node);
|
||||
Toy_freeASTNode(node);
|
||||
node = Toy_scanParser(&parser);
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
//get the bytecode dump
|
||||
int size = 0;
|
||||
unsigned char* tb = collateCompiler(&compiler, &size);
|
||||
size_t size = 0;
|
||||
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
||||
|
||||
//run the bytecode
|
||||
runInterpreter(&interpreter, tb, size);
|
||||
Toy_runInterpreter(&interpreter, tb, size);
|
||||
}
|
||||
|
||||
//clean up this iteration
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
error = false;
|
||||
|
||||
if (initialInput) {
|
||||
free((void*)initialInput);
|
||||
initialInput = NULL;
|
||||
|
||||
if (interpreter.panic) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeInterpreter(&interpreter);
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
//entry point
|
||||
int main(int argc, const char* argv[]) {
|
||||
initCommand(argc, argv);
|
||||
Toy_initCommandLine(argc, argv);
|
||||
|
||||
//command specific actions
|
||||
if (command.error) {
|
||||
usageCommand(argc, argv);
|
||||
//setup the drive system (for filesystem access)
|
||||
Toy_initDriveSystem();
|
||||
Toy_setDrivePath("scripts", "scripts");
|
||||
|
||||
//command line specific actions
|
||||
if (Toy_commandLine.error) {
|
||||
Toy_usageCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.help) {
|
||||
helpCommand(argc, argv);
|
||||
if (Toy_commandLine.help) {
|
||||
Toy_helpCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.version) {
|
||||
copyrightCommand(argc, argv);
|
||||
if (Toy_commandLine.version) {
|
||||
Toy_copyrightCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: remove this when the interpreter meets the specification
|
||||
if (command.verbose) {
|
||||
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||
//version
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d, built '%s'\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
}
|
||||
|
||||
//run source file
|
||||
if (command.sourcefile) {
|
||||
runSourceFile(command.sourcefile);
|
||||
if (Toy_commandLine.sourcefile) {
|
||||
//only works on toy files
|
||||
const char* s = strrchr(Toy_commandLine.sourcefile, '.');
|
||||
if (!s || strcmp(s, ".toy")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//run the source file
|
||||
Toy_runSourceFile(Toy_commandLine.sourcefile);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run from stdin
|
||||
if (command.source) {
|
||||
runSource(command.source);
|
||||
if (Toy_commandLine.source) {
|
||||
Toy_runSource(Toy_commandLine.source);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//compile source file
|
||||
if (command.compilefile && command.outfile) {
|
||||
if (Toy_commandLine.compilefile && Toy_commandLine.outfile) {
|
||||
//only works on toy and tb files
|
||||
const char* c = strrchr(Toy_commandLine.compilefile, '.');
|
||||
if (!c || strcmp(c, ".toy")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], c);
|
||||
return -1;
|
||||
}
|
||||
const char* o = strrchr(Toy_commandLine.outfile, '.');
|
||||
if (!o || strcmp(o, ".tb")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.tb', found '%s')" TOY_CC_RESET, argv[0], o);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//compile and save
|
||||
size_t size = 0;
|
||||
char* source = readFile(command.compilefile, &size);
|
||||
unsigned char* tb = compileString(source, &size);
|
||||
const char* source = (const char*)Toy_readFile(Toy_commandLine.compilefile, &size);
|
||||
if (!source) {
|
||||
return 1;
|
||||
}
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return 1;
|
||||
}
|
||||
writeFile(command.outfile, tb, size);
|
||||
Toy_writeFile(Toy_commandLine.outfile, tb, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run binary
|
||||
if (command.binaryfile) {
|
||||
runBinaryFile(command.binaryfile);
|
||||
if (Toy_commandLine.binaryfile) {
|
||||
//only works on tb files
|
||||
const char* c = strrchr(Toy_commandLine.binaryfile, '.');
|
||||
if (!c || strcmp(c, ".tb")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.tb', found '%s')" TOY_CC_RESET, argv[0], c); //this one is never seen
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Toy_commandLine.parseBytecodeHeader) {
|
||||
//only parse the bytecode header
|
||||
Toy_parseBinaryFileHeader(Toy_commandLine.binaryfile);
|
||||
}
|
||||
else {
|
||||
//run the binary file
|
||||
Toy_runBinaryFile(Toy_commandLine.binaryfile);
|
||||
}
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
repl();
|
||||
const char* initialSource = NULL;
|
||||
if (Toy_commandLine.initialfile) {
|
||||
//only works on toy files
|
||||
const char* s = strrchr(Toy_commandLine.initialfile, '.');
|
||||
if (!s || strcmp(s, ".toy")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
initialSource = (const char*)Toy_readFile(Toy_commandLine.initialfile, &size);
|
||||
}
|
||||
|
||||
repl(initialSource);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+123
-59
@@ -1,44 +1,46 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_about.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_timer.h"
|
||||
#include "lib_random.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "console_colors.h"
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "interpreter.h"
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_compiler.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//IO functions
|
||||
char* readFile(char* path, size_t* fileSize) {
|
||||
const unsigned char* Toy_readFile(const char* path, size_t* fileSize) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(file, 0L, SEEK_END);
|
||||
*fileSize = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* buffer = (char*)malloc(*fileSize + 1);
|
||||
unsigned char* buffer = (unsigned char*)malloc(*fileSize + 1);
|
||||
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
||||
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
|
||||
|
||||
if (bytesRead < *fileSize) {
|
||||
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
@@ -46,98 +48,160 @@ char* readFile(char* path, size_t* fileSize) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void writeFile(char* path, unsigned char* bytes, size_t size) {
|
||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
|
||||
FILE* file = fopen(path, "wb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int written = fwrite(bytes, size, 1, file);
|
||||
size_t written = fwrite(bytes, size, 1, file);
|
||||
|
||||
if (written != 1) {
|
||||
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
|
||||
exit(-1);
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//repl functions
|
||||
unsigned char* compileString(char* source, size_t* size) {
|
||||
Lexer lexer;
|
||||
Parser parser;
|
||||
Compiler compiler;
|
||||
const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
|
||||
initLexer(&lexer, source);
|
||||
initParser(&parser, &lexer);
|
||||
initCompiler(&compiler);
|
||||
Toy_initLexer(&lexer, source);
|
||||
Toy_initParser(&parser, &lexer);
|
||||
Toy_initCompiler(&compiler);
|
||||
|
||||
//run the parser until the end of the source
|
||||
ASTNode* node = scanParser(&parser);
|
||||
//step 1 - run the parser until the end of the source
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//pack up and leave
|
||||
if (node->type == AST_NODE_ERROR) {
|
||||
printf(ERROR "error node detected\n" RESET);
|
||||
freeASTNode(node);
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
//on error, pack up and leave
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
Toy_freeASTNode(node);
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
writeCompiler(&compiler, node);
|
||||
freeASTNode(node);
|
||||
node = scanParser(&parser);
|
||||
Toy_writeCompiler(&compiler, node);
|
||||
Toy_freeASTNode(node);
|
||||
node = Toy_scanParser(&parser);
|
||||
}
|
||||
|
||||
//get the bytecode dump
|
||||
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
|
||||
//step 2 - get the bytecode dump
|
||||
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
||||
|
||||
//cleanup
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
//no lexer to clean up
|
||||
|
||||
//finally
|
||||
return tb;
|
||||
}
|
||||
|
||||
void runBinary(unsigned char* tb, size_t size) {
|
||||
Interpreter interpreter;
|
||||
initInterpreter(&interpreter);
|
||||
void Toy_runBinary(const unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
runInterpreter(&interpreter, tb, size);
|
||||
freeInterpreter(&interpreter);
|
||||
Toy_runInterpreter(&interpreter, tb, (int)size);
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void runBinaryFile(char* fname) {
|
||||
void Toy_runBinaryFile(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
unsigned char* tb = (unsigned char*)readFile(fname, &size);
|
||||
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
runBinary(tb, size);
|
||||
Toy_runBinary(tb, size);
|
||||
//interpreter takes ownership of the binary data
|
||||
}
|
||||
|
||||
void runSource(char* source) {
|
||||
void Toy_runSource(const char* source) {
|
||||
size_t size = 0;
|
||||
unsigned char* tb = compileString(source, &size);
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
|
||||
runBinary(tb, size);
|
||||
Toy_runBinary(tb, size);
|
||||
}
|
||||
|
||||
void runSourceFile(char* fname) {
|
||||
void Toy_runSourceFile(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
char* source = readFile(fname, &size);
|
||||
runSource(source);
|
||||
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
Toy_runSource(source);
|
||||
free((void*)source);
|
||||
}
|
||||
|
||||
//utils for debugging the header
|
||||
static unsigned char readByte(const unsigned char* tb, int* count) {
|
||||
unsigned char ret = *(unsigned char*)(tb + *count);
|
||||
*count += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* readString(const unsigned char* tb, int* count) {
|
||||
const unsigned char* ret = tb + *count;
|
||||
*count += strlen((char*)ret) + 1; //+1 for null character
|
||||
return (const char*)ret;
|
||||
}
|
||||
|
||||
void Toy_parseBinaryFileHeader(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||
if (!tb || size < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
//header section
|
||||
const unsigned char major = readByte(tb, &count);
|
||||
const unsigned char minor = readByte(tb, &count);
|
||||
const unsigned char patch = readByte(tb, &count);
|
||||
|
||||
const char* build = readString(tb, &count);
|
||||
|
||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (interpreter built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
|
||||
printf("Toy Programming Language Bytecode Version ");
|
||||
|
||||
//print the output
|
||||
if (major == TOY_VERSION_MAJOR && minor == TOY_VERSION_MINOR && patch == TOY_VERSION_PATCH) {
|
||||
printf("%d.%d.%d", major, minor, patch);
|
||||
}
|
||||
else {
|
||||
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%d.%d.%d" TOY_CC_RESET, major, minor, patch);
|
||||
}
|
||||
|
||||
printf(" (interpreter built on ");
|
||||
|
||||
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD)) == 0) {
|
||||
printf("%s", build);
|
||||
}
|
||||
else {
|
||||
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%s" TOY_CC_RESET, build);
|
||||
}
|
||||
|
||||
printf(")\n");
|
||||
|
||||
//cleanup
|
||||
free((void*)tb);
|
||||
}
|
||||
+8
-7
@@ -2,13 +2,14 @@
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
char* readFile(char* path, size_t* fileSize);
|
||||
void writeFile(char* path, unsigned char* bytes, size_t size);
|
||||
const unsigned char* Toy_readFile(const char* path, size_t* fileSize);
|
||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
|
||||
|
||||
unsigned char* compileString(char* source, size_t* size);
|
||||
const unsigned char* Toy_compileString(const char* source, size_t* size);
|
||||
|
||||
void runBinary(unsigned char* tb, size_t size);
|
||||
void runBinaryFile(char* fname);
|
||||
void runSource(char* source);
|
||||
void runSourceFile(char* fname);
|
||||
void Toy_runBinary(const unsigned char* tb, size_t size);
|
||||
void Toy_runBinaryFile(const char* fname);
|
||||
void Toy_runSource(const char* source);
|
||||
void Toy_runSourceFile(const char* fname);
|
||||
|
||||
void Toy_parseBinaryFileHeader(const char* fname);
|
||||
@@ -1,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";
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//memoize the fib function
|
||||
var memo: [int : int] = [:];
|
||||
|
||||
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 < 40; i++) {
|
||||
var res = fib(i);
|
||||
print string i + ": " + string res;
|
||||
}
|
||||
+4
-6
@@ -1,12 +1,10 @@
|
||||
//WARNING: please think twice before using this in a test
|
||||
fn fib(n : int) {
|
||||
if (n < 2) {
|
||||
return n;
|
||||
}
|
||||
|
||||
return fib(n-1) + fib(n-2);
|
||||
if (n < 2) return n;
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 20; i++) {
|
||||
for (var i = 0; i <= 35; i++) {
|
||||
var res = fib(i);
|
||||
print string i + ": " + string res;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
|
||||
How to run this program:
|
||||
|
||||
toyrepl -n -t scripts/level.toy
|
||||
|
||||
How to move around:
|
||||
|
||||
move(up);
|
||||
move(down);
|
||||
move(left);
|
||||
move(right);
|
||||
|
||||
*/
|
||||
|
||||
//constants
|
||||
var WIDTH: int const = 12;
|
||||
var HEIGHT: int const = 12;
|
||||
|
||||
//WIDTH * HEIGHT in size
|
||||
var tiles: [[int]] const = [
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] //BUG: map is twisted along this diagonal
|
||||
];
|
||||
|
||||
var tileset: [int: string] const = [
|
||||
0: " ",
|
||||
1: "X "
|
||||
];
|
||||
|
||||
//variables
|
||||
var posX: int = 4;
|
||||
var posY: int = 4;
|
||||
|
||||
//functions
|
||||
fn draw() {
|
||||
for (var j: int = 0; j < HEIGHT; j++) {
|
||||
for (var i: int = 0; i < WIDTH; i++) {
|
||||
//draw the player pos
|
||||
if (i == posX && j == posY) {
|
||||
print "O ";
|
||||
continue;
|
||||
}
|
||||
|
||||
print tileset[ tiles[i][j] ];
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
fn moveRelative(xrel: int, yrel: int) {
|
||||
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1 || (xrel != 0 && yrel != 0)) {
|
||||
print "too fast!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (tiles[posX + xrel][posY + yrel] > 0) {
|
||||
print "Can't move that way\n";
|
||||
return;
|
||||
}
|
||||
|
||||
posX += xrel;
|
||||
posY += yrel;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
//wrap for easy use
|
||||
var up: [int] const = [0, -1];
|
||||
var down: [int] const = [0, 1];
|
||||
var left: [int] const = [-1, 0];
|
||||
var right: [int] const = [1, 0];
|
||||
|
||||
fn move(dir: [int] const) {
|
||||
return moveRelative(dir[0], dir[1]);
|
||||
}
|
||||
|
||||
//initial display
|
||||
move([0, 0]);
|
||||
@@ -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();
|
||||
}
|
||||
+40
-57
@@ -1,66 +1,49 @@
|
||||
var size: int const = 100;
|
||||
//number of iterations
|
||||
var SIZE: int const = 100;
|
||||
|
||||
var prev = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
prev.push(false);
|
||||
//lookup table
|
||||
var lookup = [
|
||||
"*": [
|
||||
"*": [
|
||||
"*": " ",
|
||||
" ": "*"
|
||||
],
|
||||
" ": [
|
||||
"*": "*",
|
||||
" ": " "
|
||||
]
|
||||
], " ": [
|
||||
"*": [
|
||||
"*": "*",
|
||||
" ": "*"
|
||||
],
|
||||
" ": [
|
||||
"*": "*",
|
||||
" ": " "
|
||||
]
|
||||
]];
|
||||
|
||||
//initial line to build from
|
||||
var prev: string = "";
|
||||
for (var i = 0; i < SIZE -1; i++) {
|
||||
prev += " ";
|
||||
}
|
||||
prev += "*"; //initial
|
||||
print prev;
|
||||
|
||||
prev.set(size - 1, true);
|
||||
//run
|
||||
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
||||
//left
|
||||
var output = (lookup[" "][prev[0]][prev[1]]);
|
||||
|
||||
|
||||
fn calc(p, i) {
|
||||
if (p[i-1] && p[i] && p[i+1]) {
|
||||
return false;
|
||||
//middle
|
||||
for (var i = 1; i < SIZE-1; i++) {
|
||||
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
|
||||
}
|
||||
|
||||
if (p[i-1] && p[i] && !p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p[i-1] && !p[i] && p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p[i-1] && !p[i] && !p[i+1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p[i-1] && p[i] && p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p[i-1] && p[i] && !p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p[i-1] && !p[i] && p[i+1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p[i-1] && !p[i] && !p[i+1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (var iteration = 0; iteration < 100; iteration++) {
|
||||
var line = [false];
|
||||
for (var i = 1; i < size-1; i++) {
|
||||
line.push(calc(prev, i));
|
||||
}
|
||||
line.push(false);
|
||||
|
||||
var output = "";
|
||||
for (var i = 0; i < line.length(); i++) {
|
||||
if (line[i]) {
|
||||
output += "*";
|
||||
}
|
||||
else {
|
||||
output += " ";
|
||||
}
|
||||
}
|
||||
//right
|
||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||
|
||||
print output;
|
||||
prev = line;
|
||||
prev = output;
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import timer;
|
||||
|
||||
var a = createTimer(1, 0);
|
||||
var b = createTimer(2, 0);
|
||||
|
||||
print a.compareTimer(b).timerToString();
|
||||
print b.compareTimer(a).timerToString();
|
||||
|
||||
var c = createTimer(0, 1);
|
||||
var d = createTimer(0, 2);
|
||||
|
||||
print c.compareTimer(d).timerToString();
|
||||
print d.compareTimer(c).timerToString();
|
||||
@@ -1,383 +0,0 @@
|
||||
#include "ast_node.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void freeASTNodeCustom(ASTNode* node, bool freeSelf) {
|
||||
//don't free a NULL node
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(node->type) {
|
||||
case AST_NODE_ERROR:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case AST_NODE_LITERAL:
|
||||
freeLiteral(node->atomic.literal);
|
||||
break;
|
||||
|
||||
case AST_NODE_UNARY:
|
||||
freeASTNode(node->unary.child);
|
||||
break;
|
||||
|
||||
case AST_NODE_BINARY:
|
||||
freeASTNode(node->binary.left);
|
||||
freeASTNode(node->binary.right);
|
||||
break;
|
||||
|
||||
case AST_NODE_GROUPING:
|
||||
freeASTNode(node->grouping.child);
|
||||
break;
|
||||
|
||||
case AST_NODE_BLOCK:
|
||||
for (int i = 0; i < node->block.count; i++) {
|
||||
freeASTNodeCustom(node->block.nodes + i, false);
|
||||
}
|
||||
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
|
||||
break;
|
||||
|
||||
case AST_NODE_COMPOUND:
|
||||
for (int i = 0; i < node->compound.count; i++) {
|
||||
freeASTNodeCustom(node->compound.nodes + i, false);
|
||||
}
|
||||
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
|
||||
break;
|
||||
|
||||
case AST_NODE_PAIR:
|
||||
freeASTNode(node->pair.left);
|
||||
freeASTNode(node->pair.right);
|
||||
break;
|
||||
|
||||
case AST_NODE_INDEX:
|
||||
freeASTNode(node->index.first);
|
||||
freeASTNode(node->index.second);
|
||||
freeASTNode(node->index.third);
|
||||
break;
|
||||
|
||||
case AST_NODE_VAR_DECL:
|
||||
freeLiteral(node->varDecl.identifier);
|
||||
freeLiteral(node->varDecl.typeLiteral);
|
||||
freeASTNode(node->varDecl.expression);
|
||||
break;
|
||||
|
||||
case AST_NODE_FN_COLLECTION:
|
||||
for (int i = 0; i < node->fnCollection.count; i++) {
|
||||
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
||||
}
|
||||
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
||||
break;
|
||||
|
||||
case AST_NODE_FN_DECL:
|
||||
freeLiteral(node->fnDecl.identifier);
|
||||
freeASTNode(node->fnDecl.arguments);
|
||||
freeASTNode(node->fnDecl.returns);
|
||||
freeASTNode(node->fnDecl.block);
|
||||
break;
|
||||
|
||||
case AST_NODE_FN_CALL:
|
||||
freeASTNode(node->fnCall.arguments);
|
||||
break;
|
||||
|
||||
case AST_NODE_FN_RETURN:
|
||||
freeASTNode(node->returns.returns);
|
||||
break;
|
||||
|
||||
case AST_NODE_IF:
|
||||
freeASTNode(node->pathIf.condition);
|
||||
freeASTNode(node->pathIf.thenPath);
|
||||
freeASTNode(node->pathIf.elsePath);
|
||||
break;
|
||||
|
||||
case AST_NODE_WHILE:
|
||||
freeASTNode(node->pathWhile.condition);
|
||||
freeASTNode(node->pathWhile.thenPath);
|
||||
break;
|
||||
|
||||
case AST_NODE_FOR:
|
||||
freeASTNode(node->pathFor.preClause);
|
||||
freeASTNode(node->pathFor.postClause);
|
||||
freeASTNode(node->pathFor.condition);
|
||||
freeASTNode(node->pathFor.thenPath);
|
||||
break;
|
||||
|
||||
case AST_NODE_BREAK:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case AST_NODE_CONTINUE:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case AST_NODE_PREFIX_INCREMENT:
|
||||
freeLiteral(node->prefixIncrement.identifier);
|
||||
break;
|
||||
case AST_NODE_PREFIX_DECREMENT:
|
||||
freeLiteral(node->prefixDecrement.identifier);
|
||||
break;
|
||||
case AST_NODE_POSTFIX_INCREMENT:
|
||||
freeLiteral(node->postfixIncrement.identifier);
|
||||
break;
|
||||
case AST_NODE_POSTFIX_DECREMENT:
|
||||
freeLiteral(node->postfixDecrement.identifier);
|
||||
break;
|
||||
|
||||
case AST_NODE_IMPORT:
|
||||
freeLiteral(node->import.identifier);
|
||||
freeLiteral(node->import.alias);
|
||||
break;
|
||||
|
||||
case AST_NODE_EXPORT:
|
||||
freeLiteral(node->export.identifier);
|
||||
freeLiteral(node->export.alias);
|
||||
break;
|
||||
}
|
||||
|
||||
if (freeSelf) {
|
||||
FREE(ASTNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
void freeASTNode(ASTNode* node) {
|
||||
freeASTNodeCustom(node, true);
|
||||
}
|
||||
|
||||
//various emitters
|
||||
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
|
||||
//allocate a new node
|
||||
*nodeHandle = ALLOCATE(ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = AST_NODE_LITERAL;
|
||||
(*nodeHandle)->atomic.literal = copyLiteral(literal);
|
||||
}
|
||||
|
||||
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
|
||||
//allocate a new node
|
||||
*nodeHandle = ALLOCATE(ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = AST_NODE_UNARY;
|
||||
(*nodeHandle)->unary.opcode = opcode;
|
||||
(*nodeHandle)->unary.child = child;
|
||||
}
|
||||
|
||||
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_BINARY;
|
||||
tmp->binary.opcode = opcode;
|
||||
tmp->binary.left = *nodeHandle;
|
||||
tmp->binary.right = rhs;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeGrouping(ASTNode** nodeHandle) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_GROUPING;
|
||||
tmp->grouping.child = *nodeHandle;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeBlock(ASTNode** nodeHandle) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_BLOCK;
|
||||
tmp->block.nodes = NULL; //NOTE: appended by the parser
|
||||
tmp->block.capacity = 0;
|
||||
tmp->block.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_COMPOUND;
|
||||
tmp->compound.literalType = literalType;
|
||||
tmp->compound.nodes = NULL;
|
||||
tmp->compound.capacity = 0;
|
||||
tmp->compound.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right) {
|
||||
//set - assume the node has already been allocated
|
||||
node->type = AST_NODE_PAIR;
|
||||
node->pair.left = left;
|
||||
node->pair.right = right;
|
||||
}
|
||||
|
||||
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_INDEX;
|
||||
tmp->index.first = first;
|
||||
tmp->index.second = second;
|
||||
tmp->index.third = third;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_VAR_DECL;
|
||||
tmp->varDecl.identifier = identifier;
|
||||
tmp->varDecl.typeLiteral = typeLiteral;
|
||||
tmp->varDecl.expression = expression;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_FN_COLLECTION;
|
||||
tmp->fnCollection.nodes = NULL;
|
||||
tmp->fnCollection.capacity = 0;
|
||||
tmp->fnCollection.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_FN_DECL;
|
||||
tmp->fnDecl.identifier = identifier;
|
||||
tmp->fnDecl.arguments = arguments;
|
||||
tmp->fnDecl.returns = returns;
|
||||
tmp->fnDecl.block = block;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeFnCall(ASTNode** nodeHandle, ASTNode* arguments) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_FN_CALL;
|
||||
tmp->fnCall.arguments = arguments;
|
||||
tmp->fnCall.argumentCount = arguments->fnCollection.count;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeFnReturn(ASTNode** nodeHandle, ASTNode* returns) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_FN_RETURN;
|
||||
tmp->returns.returns = returns;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeIf(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_IF;
|
||||
tmp->pathIf.condition = condition;
|
||||
tmp->pathIf.thenPath = thenPath;
|
||||
tmp->pathIf.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeWhile(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_WHILE;
|
||||
tmp->pathWhile.condition = condition;
|
||||
tmp->pathWhile.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeFor(ASTNode** nodeHandle, ASTNode* preClause, ASTNode* condition, ASTNode* postClause, ASTNode* thenPath) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_FOR;
|
||||
tmp->pathFor.preClause = preClause;
|
||||
tmp->pathFor.condition = condition;
|
||||
tmp->pathFor.postClause = postClause;
|
||||
tmp->pathFor.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeBreak(ASTNode** nodeHandle) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_BREAK;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeContinue(ASTNode** nodeHandle) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_CONTINUE;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_PREFIX_INCREMENT;
|
||||
tmp->prefixIncrement.identifier = copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodePrefixDecrement(ASTNode** nodeHandle, Literal identifier) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_PREFIX_DECREMENT;
|
||||
tmp->prefixDecrement.identifier = copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_POSTFIX_INCREMENT;
|
||||
tmp->postfixIncrement.identifier = copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodePostfixDecrement(ASTNode** nodeHandle, Literal identifier) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_POSTFIX_DECREMENT;
|
||||
tmp->postfixDecrement.identifier = copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_IMPORT;
|
||||
tmp->import.identifier = copyLiteral(identifier);
|
||||
tmp->import.alias = copyLiteral(alias);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void emitASTNodeExport(ASTNode** nodeHandle, Literal identifier, Literal alias) {
|
||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||
|
||||
tmp->type = AST_NODE_EXPORT;
|
||||
tmp->export.identifier = copyLiteral(identifier);
|
||||
tmp->export.alias = copyLiteral(alias);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "literal.h"
|
||||
#include "opcodes.h"
|
||||
#include "token_types.h"
|
||||
|
||||
//nodes are the intermediaries between parsers and compilers
|
||||
typedef union _node ASTNode;
|
||||
|
||||
typedef enum ASTNodeType {
|
||||
AST_NODE_ERROR,
|
||||
AST_NODE_LITERAL, //a simple value
|
||||
AST_NODE_UNARY, //one child + opcode
|
||||
AST_NODE_BINARY, //two children, left and right + opcode
|
||||
AST_NODE_GROUPING, //one child
|
||||
AST_NODE_BLOCK, //contains a sub-node array
|
||||
AST_NODE_COMPOUND, //contains a sub-node array
|
||||
AST_NODE_PAIR, //contains a left and right
|
||||
AST_NODE_INDEX, //index a variable
|
||||
AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
|
||||
AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
|
||||
AST_NODE_FN_COLLECTION, //parts of a function
|
||||
AST_NODE_FN_CALL, //call a function
|
||||
AST_NODE_FN_RETURN, //for control flow
|
||||
AST_NODE_IF, //for control flow
|
||||
AST_NODE_WHILE, //for control flow
|
||||
AST_NODE_FOR, //for control flow
|
||||
AST_NODE_BREAK, //for control flow
|
||||
AST_NODE_CONTINUE, //for control flow
|
||||
AST_NODE_PREFIX_INCREMENT, //increment a variable
|
||||
AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||
AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||
AST_NODE_POSTFIX_DECREMENT, //decrement a variable
|
||||
AST_NODE_IMPORT, //import a variable
|
||||
AST_NODE_EXPORT, //export a variable
|
||||
} ASTNodeType;
|
||||
|
||||
//literals
|
||||
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
|
||||
|
||||
typedef struct NodeLiteral {
|
||||
ASTNodeType type;
|
||||
Literal literal;
|
||||
} NodeLiteral;
|
||||
|
||||
//unary operator
|
||||
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
|
||||
|
||||
typedef struct NodeUnary {
|
||||
ASTNodeType type;
|
||||
Opcode opcode;
|
||||
ASTNode* child;
|
||||
} NodeUnary;
|
||||
|
||||
//binary operator
|
||||
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
|
||||
|
||||
typedef struct NodeBinary {
|
||||
ASTNodeType type;
|
||||
Opcode opcode;
|
||||
ASTNode* left;
|
||||
ASTNode* right;
|
||||
} NodeBinary;
|
||||
|
||||
//grouping of other AST nodes
|
||||
void emitASTNodeGrouping(ASTNode** nodeHandle);
|
||||
|
||||
typedef struct NodeGrouping {
|
||||
ASTNodeType type;
|
||||
ASTNode* child;
|
||||
} NodeGrouping;
|
||||
|
||||
//block of statement nodes
|
||||
void emitASTNodeBlock(ASTNode** nodeHandle);
|
||||
|
||||
typedef struct NodeBlock {
|
||||
ASTNodeType type;
|
||||
ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} NodeBlock;
|
||||
|
||||
//compound literals (array, dictionary)
|
||||
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
|
||||
|
||||
typedef struct NodeCompound {
|
||||
ASTNodeType type;
|
||||
LiteralType literalType;
|
||||
ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} NodeCompound;
|
||||
|
||||
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right); //NOTE: this is a set function, not an emit function
|
||||
|
||||
typedef struct NodePair {
|
||||
ASTNodeType type;
|
||||
ASTNode* left;
|
||||
ASTNode* right;
|
||||
} NodePair;
|
||||
|
||||
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
|
||||
|
||||
typedef struct NodeIndex {
|
||||
ASTNodeType type;
|
||||
ASTNode* first;
|
||||
ASTNode* second;
|
||||
ASTNode* third;
|
||||
} NodeIndex;
|
||||
|
||||
//variable declaration
|
||||
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
|
||||
|
||||
typedef struct NodeVarDecl {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
Literal typeLiteral;
|
||||
ASTNode* expression;
|
||||
} NodeVarDecl;
|
||||
|
||||
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
|
||||
void emitASTNodeFnCollection(ASTNode** nodeHandle);
|
||||
|
||||
typedef struct NodeFnCollection {
|
||||
ASTNodeType type;
|
||||
ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} NodeFnCollection;
|
||||
|
||||
//function declaration
|
||||
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
|
||||
|
||||
typedef struct NodeFnDecl {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
ASTNode* arguments;
|
||||
ASTNode* returns;
|
||||
ASTNode* block;
|
||||
} NodeFnDecl;
|
||||
|
||||
//function call
|
||||
void emitASTNodeFnCall(ASTNode** nodeHandle, ASTNode* arguments);
|
||||
|
||||
typedef struct NodeFnCall {
|
||||
ASTNodeType type;
|
||||
ASTNode* arguments;
|
||||
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
|
||||
} NodeFnCall;
|
||||
|
||||
//function return
|
||||
void emitASTNodeFnReturn(ASTNode** nodeHandle, ASTNode* returns);
|
||||
|
||||
typedef struct NodeFnReturn {
|
||||
ASTNodeType type;
|
||||
ASTNode* returns;
|
||||
} NodeFnReturn;
|
||||
|
||||
//control flow path - if-else, while, for, break, continue, return
|
||||
void emitASTNodeIf(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
|
||||
void emitASTNodeWhile(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath);
|
||||
void emitASTNodeFor(ASTNode** nodeHandle, ASTNode* preClause, ASTNode* condition, ASTNode* postClause, ASTNode* thenPath);
|
||||
void emitASTNodeBreak(ASTNode** nodeHandle);
|
||||
void emitASTNodeContinue(ASTNode** nodeHandle);
|
||||
|
||||
typedef struct NodeIf {
|
||||
ASTNodeType type;
|
||||
ASTNode* condition;
|
||||
ASTNode* thenPath;
|
||||
ASTNode* elsePath;
|
||||
} NodeIf;
|
||||
|
||||
typedef struct NodeWhile {
|
||||
ASTNodeType type;
|
||||
ASTNode* condition;
|
||||
ASTNode* thenPath;
|
||||
} NodeWhile;
|
||||
|
||||
typedef struct NodeFor {
|
||||
ASTNodeType type;
|
||||
ASTNode* preClause;
|
||||
ASTNode* condition;
|
||||
ASTNode* postClause;
|
||||
ASTNode* thenPath;
|
||||
} NodeFor;
|
||||
|
||||
typedef struct NodeBreak {
|
||||
ASTNodeType type;
|
||||
} NodeBreak;
|
||||
|
||||
typedef struct NodeContinue {
|
||||
ASTNodeType type;
|
||||
} NodeContinue;
|
||||
|
||||
//pre-post increment/decrement
|
||||
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier);
|
||||
void emitASTNodePrefixDecrement(ASTNode** nodeHandle, Literal identifier);
|
||||
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier);
|
||||
void emitASTNodePostfixDecrement(ASTNode** nodeHandle, Literal identifier);
|
||||
|
||||
typedef struct NodePrefixIncrement {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
} NodePrefixIncrement;
|
||||
|
||||
typedef struct NodePrefixDecrement {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
} NodePrefixDecrement;
|
||||
|
||||
typedef struct NodePostfixIncrement {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
} NodePostfixIncrement;
|
||||
|
||||
typedef struct NodePostfixDecrement {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
} NodePostfixDecrement;
|
||||
|
||||
//import/export a variable
|
||||
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias);
|
||||
void emitASTNodeExport(ASTNode** nodeHandle, Literal identifier, Literal alias);
|
||||
|
||||
typedef struct NodeImport {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
Literal alias;
|
||||
} NodeImport;
|
||||
|
||||
typedef struct NodeExport {
|
||||
ASTNodeType type;
|
||||
Literal identifier;
|
||||
Literal alias;
|
||||
} NodeExport;
|
||||
|
||||
union _node {
|
||||
ASTNodeType type;
|
||||
NodeLiteral atomic;
|
||||
NodeUnary unary;
|
||||
NodeBinary binary;
|
||||
NodeGrouping grouping;
|
||||
NodeBlock block;
|
||||
NodeCompound compound;
|
||||
NodePair pair;
|
||||
NodeIndex index;
|
||||
NodeVarDecl varDecl;
|
||||
NodeFnCollection fnCollection;
|
||||
NodeFnDecl fnDecl;
|
||||
NodeFnCall fnCall;
|
||||
NodeFnReturn returns;
|
||||
NodeIf pathIf; //TODO: rename these to ifStmt?
|
||||
NodeWhile pathWhile;
|
||||
NodeFor pathFor;
|
||||
NodeBreak pathBreak;
|
||||
NodeContinue pathContinue;
|
||||
NodePrefixIncrement prefixIncrement;
|
||||
NodePrefixDecrement prefixDecrement;
|
||||
NodePostfixIncrement postfixIncrement;
|
||||
NodePostfixDecrement postfixDecrement;
|
||||
NodeImport import;
|
||||
NodeExport export;
|
||||
};
|
||||
|
||||
TOY_API void freeASTNode(ASTNode* node);
|
||||
-1348
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
int _index(Interpreter* interpreter, LiteralArray* arguments);
|
||||
int _set(Interpreter* interpreter, LiteralArray* arguments);
|
||||
int _get(Interpreter* interpreter, LiteralArray* arguments);
|
||||
int _push(Interpreter* interpreter, LiteralArray* arguments);
|
||||
int _pop(Interpreter* interpreter, LiteralArray* arguments);
|
||||
int _length(Interpreter* interpreter, LiteralArray* arguments);
|
||||
int _clear(Interpreter* interpreter, LiteralArray* arguments);
|
||||
-1200
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "opcodes.h"
|
||||
#include "ast_node.h"
|
||||
#include "literal_array.h"
|
||||
|
||||
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
||||
typedef struct Compiler {
|
||||
LiteralArray literalCache;
|
||||
unsigned char* bytecode;
|
||||
int capacity;
|
||||
int count;
|
||||
} Compiler;
|
||||
|
||||
TOY_API void initCompiler(Compiler* compiler);
|
||||
TOY_API void writeCompiler(Compiler* compiler, ASTNode* node);
|
||||
TOY_API void freeCompiler(Compiler* compiler);
|
||||
|
||||
//embed the header, data section, code section, function section, etc.
|
||||
TOY_API unsigned char* collateCompiler(Compiler* compiler, int* size);
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//NOTE: you need both font AND background for these to work
|
||||
|
||||
//fonts color
|
||||
#define FONT_BLACK "\033[30;"
|
||||
#define FONT_RED "\033[31;"
|
||||
#define FONT_GREEN "\033[32;"
|
||||
#define FONT_YELLOW "\033[33;"
|
||||
#define FONT_BLUE "\033[34;"
|
||||
#define FONT_PURPLE "\033[35;"
|
||||
#define FONT_DGREEN "\033[6;"
|
||||
#define FONT_WHITE "\033[7;"
|
||||
#define FONT_CYAN "\x1b[36m"
|
||||
|
||||
//background color
|
||||
#define BACK_BLACK "40m"
|
||||
#define BACK_RED "41m"
|
||||
#define BACK_GREEN "42m"
|
||||
#define BACK_YELLOW "43m"
|
||||
#define BACK_BLUE "44m"
|
||||
#define BACK_PURPLE "45m"
|
||||
#define BACK_DGREEN "46m"
|
||||
#define BACK_WHITE "47m"
|
||||
|
||||
//useful
|
||||
#define NOTICE FONT_GREEN BACK_BLACK
|
||||
#define WARN FONT_YELLOW BACK_BLACK
|
||||
#define ERROR FONT_RED BACK_BLACK
|
||||
#define RESET "\033[0m"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "literal.h"
|
||||
#include "literal_array.h"
|
||||
#include "literal_dictionary.h"
|
||||
#include "scope.h"
|
||||
|
||||
typedef void (*PrintFn)(const char*);
|
||||
|
||||
//the interpreter acts depending on the bytecode instructions
|
||||
typedef struct Interpreter {
|
||||
//input
|
||||
unsigned char* bytecode;
|
||||
int length;
|
||||
int count;
|
||||
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
||||
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||
|
||||
//operation
|
||||
Scope* scope;
|
||||
LiteralArray stack;
|
||||
|
||||
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
|
||||
LiteralDictionary* exportTypes;
|
||||
LiteralDictionary* hooks;
|
||||
|
||||
//debug outputs
|
||||
PrintFn printOutput;
|
||||
PrintFn assertOutput;
|
||||
PrintFn errorOutput;
|
||||
|
||||
int depth; //don't overflow
|
||||
bool panic;
|
||||
} Interpreter;
|
||||
|
||||
//native API
|
||||
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
|
||||
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
|
||||
|
||||
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
|
||||
|
||||
TOY_API bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* arguments, LiteralArray* returns);
|
||||
TOY_API bool callFn(Interpreter* interpreter, char* name, LiteralArray* arguments, LiteralArray* returns);
|
||||
|
||||
//utilities for the host program
|
||||
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
|
||||
TOY_API void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput);
|
||||
TOY_API void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
|
||||
TOY_API void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
|
||||
|
||||
//main access
|
||||
TOY_API void initInterpreter(Interpreter* interpreter); //start of program
|
||||
TOY_API void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
|
||||
TOY_API void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||
TOY_API void freeInterpreter(Interpreter* interpreter); //end of program
|
||||
@@ -1,77 +0,0 @@
|
||||
#include "keyword_types.h"
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
KeywordType keywordTypes[] = {
|
||||
//type keywords
|
||||
{TOKEN_NULL, "null"},
|
||||
{TOKEN_BOOLEAN, "bool"},
|
||||
{TOKEN_INTEGER, "int"},
|
||||
{TOKEN_FLOAT, "float"},
|
||||
{TOKEN_STRING, "string"},
|
||||
{TOKEN_FUNCTION, "fn"},
|
||||
{TOKEN_OPAQUE, "opaque"},
|
||||
{TOKEN_ANY, "any"},
|
||||
|
||||
//other keywords
|
||||
{TOKEN_AS, "as"},
|
||||
{TOKEN_ASSERT, "assert"},
|
||||
{TOKEN_BREAK, "break"},
|
||||
{TOKEN_CLASS, "class"},
|
||||
{TOKEN_CONST, "const"},
|
||||
{TOKEN_CONTINUE, "continue"},
|
||||
{TOKEN_DO, "do"},
|
||||
{TOKEN_ELSE, "else"},
|
||||
{TOKEN_EXPORT, "export"},
|
||||
{TOKEN_FOR, "for"},
|
||||
{TOKEN_FOREACH, "foreach"},
|
||||
{TOKEN_IF, "if"},
|
||||
{TOKEN_IMPORT, "import"},
|
||||
{TOKEN_IN, "in"},
|
||||
{TOKEN_OF, "of"},
|
||||
{TOKEN_PRINT, "print"},
|
||||
{TOKEN_RETURN, "return"},
|
||||
{TOKEN_TYPE, "type"},
|
||||
{TOKEN_ASTYPE, "astype"},
|
||||
{TOKEN_TYPEOF, "typeof"},
|
||||
{TOKEN_VAR, "var"},
|
||||
{TOKEN_WHILE, "while"},
|
||||
|
||||
//literal values
|
||||
{TOKEN_LITERAL_TRUE, "true"},
|
||||
{TOKEN_LITERAL_FALSE, "false"},
|
||||
|
||||
//meta tokens
|
||||
{TOKEN_PASS, NULL},
|
||||
{TOKEN_ERROR, NULL},
|
||||
|
||||
{TOKEN_EOF, NULL},
|
||||
};
|
||||
|
||||
char* findKeywordByType(TokenType type) {
|
||||
if (type == TOKEN_EOF) {
|
||||
return "EOF";
|
||||
}
|
||||
|
||||
for(int i = 0; keywordTypes[i].keyword; i++) {
|
||||
if (keywordTypes[i].type == type) {
|
||||
return keywordTypes[i].keyword;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TokenType findTypeByKeyword(const char* keyword) {
|
||||
const int length = strlen(keyword);
|
||||
|
||||
for (int i = 0; keywordTypes[i].keyword; i++) {
|
||||
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
|
||||
return keywordTypes[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
return TOKEN_EOF;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "token_types.h"
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
char* keyword;
|
||||
} KeywordType;
|
||||
|
||||
extern KeywordType keywordTypes[];
|
||||
|
||||
char* findKeywordByType(TokenType type);
|
||||
|
||||
TokenType findTypeByKeyword(const char* keyword);
|
||||
-348
@@ -1,348 +0,0 @@
|
||||
#include "lexer.h"
|
||||
#include "console_colors.h"
|
||||
#include "keyword_types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//static generic utility functions
|
||||
static void cleanLexer(Lexer* lexer) {
|
||||
lexer->source = NULL;
|
||||
lexer->start = 0;
|
||||
lexer->current = 0;
|
||||
lexer->line = 1;
|
||||
}
|
||||
|
||||
static bool isAtEnd(Lexer* lexer) {
|
||||
return lexer->source[lexer->current] == '\0';
|
||||
}
|
||||
|
||||
static char peek(Lexer* lexer) {
|
||||
return lexer->source[lexer->current];
|
||||
}
|
||||
|
||||
static char peekNext(Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) return '\0';
|
||||
return lexer->source[lexer->current + 1];
|
||||
}
|
||||
|
||||
static char advance(Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
//new line
|
||||
if (lexer->source[lexer->current] == '\n') {
|
||||
lexer->line++;
|
||||
}
|
||||
|
||||
lexer->current++;
|
||||
return lexer->source[lexer->current - 1];
|
||||
}
|
||||
|
||||
static void eatWhitespace(Lexer* lexer) {
|
||||
const char c = peek(lexer);
|
||||
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
advance(lexer);
|
||||
break;
|
||||
|
||||
//comments
|
||||
case '/':
|
||||
//eat the line
|
||||
if (peekNext(lexer) == '/') {
|
||||
while (advance(lexer) != '\n' && !isAtEnd(lexer));
|
||||
break;
|
||||
}
|
||||
|
||||
//eat the block
|
||||
if (peekNext(lexer) == '*') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
//tail recursion
|
||||
eatWhitespace(lexer);
|
||||
}
|
||||
|
||||
static bool isDigit(Lexer* lexer) {
|
||||
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(Lexer* lexer) {
|
||||
return
|
||||
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
||||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
||||
peek(lexer) == '_'
|
||||
;
|
||||
}
|
||||
|
||||
static bool match(Lexer* lexer, char c) {
|
||||
if (peek(lexer) == c) {
|
||||
advance(lexer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//token generators
|
||||
static Token makeErrorToken(Lexer* lexer, char* msg) {
|
||||
Token token;
|
||||
|
||||
token.type = TOKEN_ERROR;
|
||||
token.lexeme = msg;
|
||||
token.length = strlen(msg);
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("err:");
|
||||
printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeToken(Lexer* lexer, TokenType type) {
|
||||
Token token;
|
||||
|
||||
token.type = type;
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.lexeme = &lexer->source[lexer->current - token.length];
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||
if (command.verbose) {
|
||||
printf("tok:");
|
||||
printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeIntegerOrFloat(Lexer* lexer) {
|
||||
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
|
||||
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
|
||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
||||
type = TOKEN_LITERAL_FLOAT;
|
||||
advance(lexer);
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
}
|
||||
|
||||
Token token;
|
||||
|
||||
token.type = type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (type == TOKEN_LITERAL_INTEGER) {
|
||||
printf("int:");
|
||||
} else {
|
||||
printf("flt:");
|
||||
}
|
||||
printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeString(Lexer* lexer, char terminator) {
|
||||
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
advance(lexer); //eat terminator
|
||||
|
||||
if (isAtEnd(lexer)) {
|
||||
return makeErrorToken(lexer, "Unterminated string");
|
||||
}
|
||||
|
||||
Token token;
|
||||
|
||||
token.type = TOKEN_LITERAL_STRING;
|
||||
token.lexeme = &lexer->source[lexer->start + 1];
|
||||
token.length = lexer->current - lexer->start - 2;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("str:");
|
||||
printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeKeywordOrIdentifier(Lexer* lexer) {
|
||||
advance(lexer); //first letter can only be alpha
|
||||
|
||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
//scan for a keyword
|
||||
for (int i = 0; keywordTypes[i].keyword; i++) {
|
||||
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||
Token token;
|
||||
|
||||
token.type = keywordTypes[i].type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("kwd:");
|
||||
printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
//return an identifier
|
||||
Token token;
|
||||
|
||||
token.type = TOKEN_IDENTIFIER;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("idf:");
|
||||
printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void initLexer(Lexer* lexer, char* source) {
|
||||
cleanLexer(lexer);
|
||||
|
||||
lexer->source = source;
|
||||
}
|
||||
|
||||
Token scanLexer(Lexer* lexer) {
|
||||
eatWhitespace(lexer);
|
||||
|
||||
lexer->start = lexer->current;
|
||||
|
||||
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
|
||||
|
||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||
|
||||
char c = advance(lexer);
|
||||
|
||||
switch(c) {
|
||||
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
|
||||
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
|
||||
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
|
||||
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
|
||||
case '[': return makeToken(lexer, TOKEN_BRACKET_LEFT);
|
||||
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
|
||||
|
||||
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
|
||||
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
|
||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
|
||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
|
||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
|
||||
|
||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
|
||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
|
||||
|
||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
|
||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
|
||||
|
||||
case '&': //TOKEN_AND not used
|
||||
if (advance(lexer) != '&') {
|
||||
return makeErrorToken(lexer, "Unexpected '&'");
|
||||
} else {
|
||||
return makeToken(lexer, TOKEN_AND);
|
||||
}
|
||||
|
||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
|
||||
|
||||
case ':': return makeToken(lexer, TOKEN_COLON);
|
||||
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
||||
case ',': return makeToken(lexer, TOKEN_COMMA);
|
||||
case '.':
|
||||
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
return makeToken(lexer, TOKEN_REST);
|
||||
}
|
||||
return makeToken(lexer, TOKEN_DOT);
|
||||
|
||||
case '"':
|
||||
return makeString(lexer, c);
|
||||
//TODO: possibly support interpolated strings
|
||||
|
||||
default: {
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "Unexpected token: %c", c);
|
||||
return makeErrorToken(lexer, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void trim(char** s, int* l) { //all this to remove a newline?
|
||||
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
||||
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
||||
}
|
||||
|
||||
//for debugging
|
||||
void printToken(Token* token) {
|
||||
if (token->type == TOKEN_ERROR) {
|
||||
printf(ERROR "Error\t%d\t%.*s\n" RESET, token->line, token->length, token->lexeme);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\t%d\t%d\t", token->type, token->line);
|
||||
|
||||
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
|
||||
printf("%.*s\t", token->length, token->lexeme);
|
||||
} else {
|
||||
char* keyword = findKeywordByType(token->type);
|
||||
|
||||
if (keyword != NULL) {
|
||||
printf("%s", keyword);
|
||||
} else {
|
||||
char* str = token->lexeme;
|
||||
int length = token->length;
|
||||
trim(&str, &length);
|
||||
printf("%.*s", length, str);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "token_types.h"
|
||||
|
||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||
typedef struct {
|
||||
char* source;
|
||||
int start; //start of the token
|
||||
int current; //current position of the lexer
|
||||
int line; //track this for error handling
|
||||
} Lexer;
|
||||
|
||||
//tokens are intermediaries between lexers and parsers
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
char* lexeme;
|
||||
int length;
|
||||
int line;
|
||||
} Token;
|
||||
|
||||
TOY_API void initLexer(Lexer* lexer, char* source);
|
||||
Token scanLexer(Lexer* lexer);
|
||||
|
||||
//for debugging
|
||||
void printToken(Token* token);
|
||||
@@ -1,695 +0,0 @@
|
||||
#include "literal.h"
|
||||
#include "memory.h"
|
||||
|
||||
#include "literal_array.h"
|
||||
#include "literal_dictionary.h"
|
||||
#include "scope.h"
|
||||
|
||||
#include "console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//hash util functions
|
||||
static unsigned int hashString(const char* string, int length) {
|
||||
unsigned int hash = 2166136261u;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
hash *= string[i];
|
||||
hash ^= 16777619;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned int hashUInt(unsigned int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void freeLiteral(Literal literal) {
|
||||
//refstrings
|
||||
if (IS_STRING(literal)) {
|
||||
deleteRefString(AS_STRING(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_IDENTIFIER(literal)) {
|
||||
deleteRefString(AS_IDENTIFIER(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//compounds
|
||||
if (IS_ARRAY(literal) || literal.type == LITERAL_DICTIONARY_INTERMEDIATE || literal.type == LITERAL_TYPE_INTERMEDIATE) {
|
||||
freeLiteralArray(AS_ARRAY(literal));
|
||||
FREE(LiteralArray, AS_ARRAY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_DICTIONARY(literal)) {
|
||||
freeLiteralDictionary(AS_DICTIONARY(literal));
|
||||
FREE(LiteralDictionary, AS_DICTIONARY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//complex literals
|
||||
if (IS_FUNCTION(literal)) {
|
||||
popScope(AS_FUNCTION(literal).scope);
|
||||
AS_FUNCTION(literal).scope = NULL;
|
||||
FREE_ARRAY(unsigned char, AS_FUNCTION(literal).bytecode, AS_FUNCTION(literal).length);
|
||||
}
|
||||
|
||||
if (IS_TYPE(literal)) {
|
||||
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
||||
freeLiteral(((Literal*)(AS_TYPE(literal).subtypes))[i]);
|
||||
}
|
||||
FREE_ARRAY(Literal, AS_TYPE(literal).subtypes, AS_TYPE(literal).capacity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isTruthy(Literal x) {
|
||||
if (IS_NULL(x)) {
|
||||
fprintf(stderr, ERROR "ERROR: Null is neither true nor false\n" RESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IS_BOOLEAN(x)) {
|
||||
return AS_BOOLEAN(x);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Literal _toStringLiteral(RefString* ptr) {
|
||||
return ((Literal){LITERAL_STRING, { .string.ptr = ptr }});
|
||||
}
|
||||
|
||||
Literal _toIdentifierLiteral(RefString* ptr) {
|
||||
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = ptr, .identifier.hash = hashString(toCString(ptr), lengthRefString(ptr)) }});
|
||||
}
|
||||
|
||||
Literal* _typePushSubtype(Literal* lit, Literal subtype) {
|
||||
//grow the subtype array
|
||||
if (AS_TYPE(*lit).count + 1 > AS_TYPE(*lit).capacity) {
|
||||
int oldCapacity = AS_TYPE(*lit).capacity;
|
||||
|
||||
AS_TYPE(*lit).capacity = GROW_CAPACITY(oldCapacity);
|
||||
AS_TYPE(*lit).subtypes = GROW_ARRAY(Literal, AS_TYPE(*lit).subtypes, oldCapacity, AS_TYPE(*lit).capacity);
|
||||
}
|
||||
|
||||
//actually push
|
||||
((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count++ ] = subtype;
|
||||
return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ];
|
||||
}
|
||||
|
||||
Literal copyLiteral(Literal original) {
|
||||
switch(original.type) {
|
||||
case LITERAL_NULL:
|
||||
case LITERAL_BOOLEAN:
|
||||
case LITERAL_INTEGER:
|
||||
case LITERAL_FLOAT:
|
||||
//no copying needed
|
||||
return original;
|
||||
|
||||
case LITERAL_STRING: {
|
||||
return TO_STRING_LITERAL(copyRefString(AS_STRING(original)));
|
||||
}
|
||||
|
||||
case LITERAL_ARRAY: {
|
||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||
initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
||||
pushLiteralArray(array, AS_ARRAY(original)->literals[i]);
|
||||
}
|
||||
|
||||
return TO_ARRAY_LITERAL(array);
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY: {
|
||||
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||
initLiteralDictionary(dictionary);
|
||||
|
||||
//copy each entry
|
||||
for (int i = 0; i < AS_DICTIONARY(original)->capacity; i++) {
|
||||
if ( !IS_NULL(AS_DICTIONARY(original)->entries[i].key) ) {
|
||||
setLiteralDictionary(dictionary, AS_DICTIONARY(original)->entries[i].key, AS_DICTIONARY(original)->entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return TO_DICTIONARY_LITERAL(dictionary);
|
||||
}
|
||||
|
||||
case LITERAL_FUNCTION: {
|
||||
unsigned char* buffer = ALLOCATE(unsigned char, AS_FUNCTION(original).length);
|
||||
memcpy(buffer, AS_FUNCTION(original).bytecode, AS_FUNCTION(original).length);
|
||||
|
||||
Literal literal = TO_FUNCTION_LITERAL(buffer, AS_FUNCTION(original).length);
|
||||
AS_FUNCTION(literal).scope = copyScope(AS_FUNCTION(original).scope);
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
case LITERAL_IDENTIFIER: {
|
||||
return TO_IDENTIFIER_LITERAL(copyRefString(AS_IDENTIFIER(original)));
|
||||
}
|
||||
|
||||
case LITERAL_TYPE: {
|
||||
Literal lit = TO_TYPE_LITERAL(AS_TYPE(original).typeOf, AS_TYPE(original).constant);
|
||||
|
||||
for (int i = 0; i < AS_TYPE(original).count; i++) {
|
||||
TYPE_PUSH_SUBTYPE(&lit, copyLiteral( ((Literal*)(AS_TYPE(original).subtypes))[i] ));
|
||||
}
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
case LITERAL_OPAQUE: {
|
||||
return original; //literally a shallow copy
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||
initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
||||
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
|
||||
pushLiteralArray(array, literal);
|
||||
freeLiteral(literal);
|
||||
}
|
||||
|
||||
Literal ret = TO_ARRAY_LITERAL(array);
|
||||
ret.type = LITERAL_DICTIONARY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case LITERAL_TYPE_INTERMEDIATE: {
|
||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||
initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
||||
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
|
||||
pushLiteralArray(array, literal);
|
||||
freeLiteral(literal);
|
||||
}
|
||||
|
||||
Literal ret = TO_ARRAY_LITERAL(array);
|
||||
ret.type = LITERAL_TYPE_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
//no copying possible
|
||||
return original;
|
||||
|
||||
default:
|
||||
fprintf(stderr, ERROR "ERROR: Can't copy that literal type: %d\n" RESET, original.type);
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
bool literalsAreEqual(Literal lhs, Literal rhs) {
|
||||
//utility for other things
|
||||
if (lhs.type != rhs.type) {
|
||||
// ints and floats are compatible
|
||||
if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) {
|
||||
if (IS_INTEGER(lhs)) {
|
||||
return AS_INTEGER(lhs) + AS_FLOAT(rhs);
|
||||
}
|
||||
else {
|
||||
return AS_FLOAT(lhs) + AS_INTEGER(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(lhs.type) {
|
||||
case LITERAL_NULL:
|
||||
return true; //can only be true because of the check above
|
||||
|
||||
case LITERAL_BOOLEAN:
|
||||
return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs);
|
||||
|
||||
case LITERAL_INTEGER:
|
||||
return AS_INTEGER(lhs) == AS_INTEGER(rhs);
|
||||
|
||||
case LITERAL_FLOAT:
|
||||
return AS_FLOAT(lhs) == AS_FLOAT(rhs);
|
||||
|
||||
case LITERAL_STRING:
|
||||
return equalsRefString(AS_STRING(lhs), AS_STRING(rhs));
|
||||
|
||||
case LITERAL_ARRAY:
|
||||
case LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
||||
case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
||||
//mismatched sizes
|
||||
if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//mismatched elements (in order)
|
||||
for (int i = 0; i < AS_ARRAY(lhs)->count; i++) {
|
||||
if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case LITERAL_DICTIONARY:
|
||||
//relatively slow, especially when nested
|
||||
for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) {
|
||||
if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
||||
//check it exists in rhs
|
||||
if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//compare the values
|
||||
Literal val = getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
|
||||
if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, val)) {
|
||||
freeLiteral(val);
|
||||
return false;
|
||||
}
|
||||
freeLiteral(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case LITERAL_FUNCTION:
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
return false; //functions are never equal
|
||||
break;
|
||||
|
||||
case LITERAL_IDENTIFIER:
|
||||
//check shortcuts
|
||||
if (HASH_I(lhs) != HASH_I(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return equalsRefString(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs));
|
||||
|
||||
case LITERAL_TYPE:
|
||||
//check types
|
||||
if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//const don't match
|
||||
if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check subtypes
|
||||
if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check array|dictionary signatures are the same (in order)
|
||||
if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) {
|
||||
for (int i = 0; i < AS_TYPE(lhs).count; i++) {
|
||||
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case LITERAL_OPAQUE:
|
||||
return false; //IDK what this is!
|
||||
|
||||
case LITERAL_ANY:
|
||||
return true;
|
||||
|
||||
case LITERAL_FUNCTION_INTERMEDIATE:
|
||||
fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET);
|
||||
return false;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int hashLiteral(Literal lit) {
|
||||
switch(lit.type) {
|
||||
case LITERAL_NULL:
|
||||
return 0;
|
||||
|
||||
case LITERAL_BOOLEAN:
|
||||
return AS_BOOLEAN(lit) ? 1 : 0;
|
||||
|
||||
case LITERAL_INTEGER:
|
||||
return hashUInt((unsigned int)AS_INTEGER(lit));
|
||||
|
||||
case LITERAL_FLOAT:
|
||||
return hashUInt(*(unsigned int*)(&AS_FLOAT(lit)));
|
||||
|
||||
case LITERAL_STRING:
|
||||
return hashString(toCString(AS_STRING(lit)), lengthRefString(AS_STRING(lit)));
|
||||
|
||||
case LITERAL_ARRAY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < AS_ARRAY(lit)->count; i++) {
|
||||
res += hashLiteral(AS_ARRAY(lit)->literals[i]);
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case LITERAL_DICTIONARY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) {
|
||||
if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
||||
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key);
|
||||
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value);
|
||||
}
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case LITERAL_FUNCTION:
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
return 0; //TODO: find a way to hash these properly
|
||||
|
||||
case LITERAL_IDENTIFIER:
|
||||
return HASH_I(lit); //pre-computed
|
||||
|
||||
case LITERAL_TYPE:
|
||||
return AS_TYPE(lit).typeOf; //nothing else I can do
|
||||
|
||||
case LITERAL_OPAQUE:
|
||||
case LITERAL_ANY:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
//should never bee seen
|
||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//utils
|
||||
static void stdoutWrapper(const char* output) {
|
||||
printf("%s", output);
|
||||
}
|
||||
|
||||
//buffer the prints
|
||||
static char* globalPrintBuffer = NULL;
|
||||
static size_t globalPrintCapacity = 0;
|
||||
static size_t globalPrintCount = 0;
|
||||
|
||||
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
||||
static char quotes = 0; //set to 0 to not show string quotes
|
||||
|
||||
static void printToBuffer(const char* str) {
|
||||
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
|
||||
int oldCapacity = globalPrintCapacity;
|
||||
|
||||
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
|
||||
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
||||
}
|
||||
|
||||
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
|
||||
globalPrintCount += strlen(str);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void printLiteral(Literal literal) {
|
||||
printLiteralCustom(literal, stdoutWrapper);
|
||||
}
|
||||
|
||||
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
||||
switch(literal.type) {
|
||||
case LITERAL_NULL:
|
||||
printFn("null");
|
||||
break;
|
||||
|
||||
case LITERAL_BOOLEAN:
|
||||
printFn(AS_BOOLEAN(literal) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case LITERAL_INTEGER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_FLOAT: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_STRING: {
|
||||
char buffer[MAX_STRING_LENGTH];
|
||||
if (!quotes) {
|
||||
snprintf(buffer, MAX_STRING_LENGTH, "%.*s", lengthRefString(AS_STRING(literal)), toCString(AS_STRING(literal)));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, lengthRefString(AS_STRING(literal)), toCString(AS_STRING(literal)), quotes);
|
||||
}
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_ARRAY: {
|
||||
LiteralArray* ptr = AS_ARRAY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->count; i++) {
|
||||
quotes = '"';
|
||||
printLiteralCustom(ptr->literals[i], printToBuffer);
|
||||
|
||||
if (i + 1 < ptr->count) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
}
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_DICTIONARY: {
|
||||
LiteralDictionary* ptr = AS_DICTIONARY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
int delimCount = 0;
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->capacity; i++) {
|
||||
if (IS_NULL(ptr->entries[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delimCount++ > 0) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
|
||||
quotes = '"';
|
||||
printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
||||
printToBuffer(":");
|
||||
quotes = '"';
|
||||
printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
||||
}
|
||||
|
||||
//empty dicts MUST have a ":" printed
|
||||
if (ptr->count == 0) {
|
||||
printToBuffer(":");
|
||||
}
|
||||
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
//TODO: functions
|
||||
case LITERAL_FUNCTION:
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
printFn("(function)");
|
||||
break;
|
||||
|
||||
case LITERAL_IDENTIFIER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%.*s", lengthRefString(AS_IDENTIFIER(literal)), toCString(AS_IDENTIFIER(literal)));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_TYPE: {
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the type correctly
|
||||
printToBuffer("<");
|
||||
|
||||
switch(AS_TYPE(literal).typeOf) {
|
||||
case LITERAL_NULL:
|
||||
printToBuffer("null");
|
||||
break;
|
||||
|
||||
case LITERAL_BOOLEAN:
|
||||
printToBuffer("bool");
|
||||
break;
|
||||
|
||||
case LITERAL_INTEGER:
|
||||
printToBuffer("int");
|
||||
break;
|
||||
|
||||
case LITERAL_FLOAT:
|
||||
printToBuffer("float");
|
||||
break;
|
||||
|
||||
case LITERAL_STRING:
|
||||
printToBuffer("string");
|
||||
break;
|
||||
|
||||
case LITERAL_ARRAY:
|
||||
//print all in the array
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
||||
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case LITERAL_DICTIONARY:
|
||||
printToBuffer("[");
|
||||
|
||||
for (int i = 0; i < AS_TYPE(literal).count; i += 2) {
|
||||
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
printToBuffer(":");
|
||||
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case LITERAL_FUNCTION:
|
||||
printToBuffer("function");
|
||||
break;
|
||||
|
||||
case LITERAL_FUNCTION_NATIVE:
|
||||
printToBuffer("native");
|
||||
break;
|
||||
|
||||
case LITERAL_IDENTIFIER:
|
||||
printToBuffer("identifier");
|
||||
break;
|
||||
|
||||
case LITERAL_TYPE:
|
||||
printToBuffer("type");
|
||||
break;
|
||||
|
||||
case LITERAL_OPAQUE:
|
||||
printToBuffer("opaque");
|
||||
break;
|
||||
|
||||
case LITERAL_ANY:
|
||||
printToBuffer("any");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf);
|
||||
}
|
||||
|
||||
//const (printed last)
|
||||
if (AS_TYPE(literal).constant) {
|
||||
printToBuffer(" const");
|
||||
}
|
||||
|
||||
printToBuffer(">");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_TYPE_INTERMEDIATE:
|
||||
case LITERAL_FUNCTION_INTERMEDIATE:
|
||||
printFn("Unprintable literal found");
|
||||
break;
|
||||
|
||||
case LITERAL_OPAQUE:
|
||||
printFn("(opaque)");
|
||||
break;
|
||||
|
||||
case LITERAL_ANY:
|
||||
printFn("(any)");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "refstring.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
LITERAL_NULL,
|
||||
LITERAL_BOOLEAN,
|
||||
LITERAL_INTEGER,
|
||||
LITERAL_FLOAT,
|
||||
LITERAL_STRING,
|
||||
LITERAL_ARRAY,
|
||||
LITERAL_DICTIONARY,
|
||||
LITERAL_FUNCTION,
|
||||
LITERAL_IDENTIFIER,
|
||||
LITERAL_TYPE,
|
||||
LITERAL_OPAQUE,
|
||||
LITERAL_ANY,
|
||||
|
||||
//these are meta-level types - not for general use
|
||||
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||
LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
||||
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||
LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
||||
LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
||||
} LiteralType;
|
||||
|
||||
typedef struct {
|
||||
LiteralType type;
|
||||
union {
|
||||
bool boolean;
|
||||
int integer;
|
||||
float number;
|
||||
struct {
|
||||
RefString* ptr;
|
||||
//string hash?
|
||||
} string;
|
||||
|
||||
void* array;
|
||||
void* dictionary;
|
||||
|
||||
struct {
|
||||
void* bytecode;
|
||||
void* scope;
|
||||
int length;
|
||||
} function;
|
||||
|
||||
struct { //for variable names
|
||||
RefString* ptr;
|
||||
int hash;
|
||||
} identifier;
|
||||
|
||||
struct {
|
||||
LiteralType typeOf; //no longer a mask
|
||||
bool constant;
|
||||
void* subtypes; //for nested types caused by compounds
|
||||
int capacity;
|
||||
int count;
|
||||
} type;
|
||||
|
||||
struct {
|
||||
void* ptr;
|
||||
int tag; //TODO: remove tags?
|
||||
} opaque;
|
||||
} as;
|
||||
} Literal;
|
||||
|
||||
#define IS_NULL(value) ((value).type == LITERAL_NULL)
|
||||
#define IS_BOOLEAN(value) ((value).type == LITERAL_BOOLEAN)
|
||||
#define IS_INTEGER(value) ((value).type == LITERAL_INTEGER)
|
||||
#define IS_FLOAT(value) ((value).type == LITERAL_FLOAT)
|
||||
#define IS_STRING(value) ((value).type == LITERAL_STRING)
|
||||
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
|
||||
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
|
||||
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
|
||||
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
|
||||
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
|
||||
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
|
||||
#define IS_OPAQUE(value) ((value).type == LITERAL_OPAQUE)
|
||||
|
||||
#define AS_BOOLEAN(value) ((value).as.boolean)
|
||||
#define AS_INTEGER(value) ((value).as.integer)
|
||||
#define AS_FLOAT(value) ((value).as.number)
|
||||
#define AS_STRING(value) ((value).as.string.ptr)
|
||||
#define AS_ARRAY(value) ((LiteralArray*)((value).as.array))
|
||||
#define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary))
|
||||
#define AS_FUNCTION(value) ((value).as.function)
|
||||
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||
#define AS_TYPE(value) ((value).as.type)
|
||||
#define AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||
|
||||
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
|
||||
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
|
||||
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
|
||||
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
|
||||
#define TO_STRING_LITERAL(value) _toStringLiteral(value)
|
||||
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
|
||||
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
|
||||
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
||||
#define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value)
|
||||
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
||||
#define TO_OPAQUE_LITERAL(value, t) ((Literal){ LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
|
||||
|
||||
TOY_API void freeLiteral(Literal literal);
|
||||
|
||||
#define IS_TRUTHY(x) _isTruthy(x)
|
||||
|
||||
#define MAX_STRING_LENGTH 4096
|
||||
#define HASH_I(lit) ((lit).as.identifier.hash)
|
||||
#define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype)
|
||||
#define OPAQUE_TAG(o) o.as.opaque.tag
|
||||
|
||||
//BUGFIX: macros are not functions
|
||||
TOY_API bool _isTruthy(Literal x);
|
||||
TOY_API Literal _toStringLiteral(RefString* ptr);
|
||||
TOY_API Literal _toIdentifierLiteral(RefString* ptr);
|
||||
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
|
||||
|
||||
//utils
|
||||
TOY_API Literal copyLiteral(Literal original);
|
||||
TOY_API bool literalsAreEqual(Literal lhs, Literal rhs);
|
||||
TOY_API int hashLiteral(Literal lit);
|
||||
|
||||
TOY_API void printLiteral(Literal literal);
|
||||
TOY_API void printLiteralCustom(Literal literal, void (printFn)(const char*));
|
||||
@@ -1,100 +0,0 @@
|
||||
#include "literal_array.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//exposed functions
|
||||
void initLiteralArray(LiteralArray* array) {
|
||||
array->capacity = 0;
|
||||
array->count = 0;
|
||||
array->literals = NULL;
|
||||
}
|
||||
|
||||
void freeLiteralArray(LiteralArray* array) {
|
||||
//clean up memory
|
||||
for(int i = 0; i < array->count; i++) {
|
||||
freeLiteral(array->literals[i]);
|
||||
}
|
||||
|
||||
FREE_ARRAY(Literal, array->literals, array->capacity);
|
||||
initLiteralArray(array);
|
||||
}
|
||||
|
||||
int pushLiteralArray(LiteralArray* array, Literal literal) {
|
||||
if (array->capacity < array->count + 1) {
|
||||
int oldCapacity = array->capacity;
|
||||
|
||||
array->capacity = GROW_CAPACITY(oldCapacity);
|
||||
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
|
||||
}
|
||||
|
||||
array->literals[array->count] = copyLiteral(literal);
|
||||
return array->count++;
|
||||
}
|
||||
|
||||
Literal popLiteralArray(LiteralArray* array) {
|
||||
if (array->count <= 0) {
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the return
|
||||
Literal ret = array->literals[array->count-1];
|
||||
|
||||
//null the existing data
|
||||
array->literals[array->count-1] = TO_NULL_LITERAL;
|
||||
|
||||
array->count--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//find a literal in the array that matches the "literal" argument
|
||||
int findLiteralIndex(LiteralArray* array, Literal literal) {
|
||||
for (int i = 0; i < array->count; i++) {
|
||||
//not the same type
|
||||
if (array->literals[i].type != literal.type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//types match?
|
||||
if (literalsAreEqual(array->literals[i], literal)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
|
||||
if (!IS_INTEGER(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: implicit push when referencing one-past-the-end?
|
||||
|
||||
freeLiteral(array->literals[idx]);
|
||||
array->literals[idx] = copyLiteral(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Literal getLiteralArray(LiteralArray* array, Literal index) {
|
||||
if (!IS_INTEGER(index)) {
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
int idx = AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
return copyLiteral(array->literals[idx]);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "literal.h"
|
||||
|
||||
typedef struct LiteralArray {
|
||||
Literal* literals;
|
||||
int capacity;
|
||||
int count;
|
||||
} LiteralArray;
|
||||
|
||||
TOY_API void initLiteralArray(LiteralArray* array);
|
||||
TOY_API void freeLiteralArray(LiteralArray* array);
|
||||
TOY_API int pushLiteralArray(LiteralArray* array, Literal literal);
|
||||
TOY_API Literal popLiteralArray(LiteralArray* array);
|
||||
TOY_API bool setLiteralArray(LiteralArray* array, Literal index, Literal value);
|
||||
TOY_API Literal getLiteralArray(LiteralArray* array, Literal index);
|
||||
|
||||
int findLiteralIndex(LiteralArray* array, Literal literal);
|
||||
@@ -1,219 +0,0 @@
|
||||
#include "literal_dictionary.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include "console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//util functions
|
||||
static void setEntryValues(_entry* entry, Literal key, Literal value) {
|
||||
//much simpler now
|
||||
freeLiteral(entry->key);
|
||||
entry->key = copyLiteral(key);
|
||||
|
||||
freeLiteral(entry->value);
|
||||
entry->value = copyLiteral(value);
|
||||
}
|
||||
|
||||
static _entry* getEntryArray(_entry* array, int capacity, Literal key, unsigned int hash, bool mustExist) {
|
||||
//find "key", starting at index
|
||||
unsigned int index = hash % capacity;
|
||||
unsigned int start = index;
|
||||
|
||||
//increment once, so it can't equal start
|
||||
index = (index + 1) % capacity;
|
||||
|
||||
//literal probing and collision checking
|
||||
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
||||
_entry* entry = &array[index];
|
||||
|
||||
if (IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
||||
if (IS_NULL(entry->value) && !mustExist) {
|
||||
//found a truly empty bucket
|
||||
return entry;
|
||||
}
|
||||
//else it's a tombstone - ignore
|
||||
} else {
|
||||
if (literalsAreEqual(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
index = (index + 1) % capacity;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
||||
//new entry space
|
||||
_entry* newEntries = ALLOCATE(_entry, capacity);
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
newEntries[i].key = TO_NULL_LITERAL;
|
||||
newEntries[i].value = TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//move the old array into the new one
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
if (IS_NULL((*dictionaryHandle)[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//place the key and value in the new array (reusing string memory)
|
||||
_entry* entry = getEntryArray(newEntries, capacity, TO_NULL_LITERAL, hashLiteral((*dictionaryHandle)[i].key), false);
|
||||
|
||||
entry->key = (*dictionaryHandle)[i].key;
|
||||
entry->value = (*dictionaryHandle)[i].value;
|
||||
}
|
||||
|
||||
//clear the old array
|
||||
FREE_ARRAY(_entry, *dictionaryHandle, oldCapacity);
|
||||
|
||||
*dictionaryHandle = newEntries;
|
||||
}
|
||||
|
||||
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
|
||||
//expand array if needed
|
||||
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
|
||||
int oldCapacity = *capacityPtr;
|
||||
*capacityPtr = GROW_CAPACITY(*capacityPtr);
|
||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||
}
|
||||
|
||||
_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
||||
|
||||
//true = contains increase
|
||||
if (IS_NULL(entry->key)) {
|
||||
setEntryValues(entry, key, value);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
setEntryValues(entry, key, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void freeEntry(_entry* entry) {
|
||||
freeLiteral(entry->key);
|
||||
freeLiteral(entry->value);
|
||||
entry->key = TO_NULL_LITERAL;
|
||||
entry->value = TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
static void freeEntryArray(_entry* array, int capacity) {
|
||||
if (array == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (!IS_NULL(array[i].key)) {
|
||||
freeEntry(&array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FREE_ARRAY(_entry, array, capacity);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void initLiteralDictionary(LiteralDictionary* dictionary) {
|
||||
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
||||
dictionary->entries = NULL;
|
||||
dictionary->capacity = GROW_CAPACITY(0);
|
||||
dictionary->contains = 0;
|
||||
dictionary->count = 0;
|
||||
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
|
||||
}
|
||||
|
||||
void freeLiteralDictionary(LiteralDictionary* dictionary) {
|
||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||
dictionary->capacity = 0;
|
||||
dictionary->contains = 0;
|
||||
}
|
||||
|
||||
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
|
||||
if (IS_NULL(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_OPAQUE(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
|
||||
|
||||
if (increment) {
|
||||
dictionary->contains++;
|
||||
dictionary->count++;
|
||||
}
|
||||
}
|
||||
|
||||
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||
if (IS_NULL(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
if (IS_OPAQUE(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
return copyLiteral(entry->value);
|
||||
}
|
||||
else {
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||
if (IS_NULL(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (remove)\n" RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (remove)\n" RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_OPAQUE(key)) {
|
||||
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
freeEntry(entry);
|
||||
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
|
||||
dictionary->count--;
|
||||
}
|
||||
}
|
||||
|
||||
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||
//null & not tombstoned
|
||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
|
||||
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "literal.h"
|
||||
|
||||
//TODO: benchmark this
|
||||
#define DICTIONARY_MAX_LOAD 0.75
|
||||
|
||||
typedef struct _entry {
|
||||
Literal key;
|
||||
Literal value;
|
||||
} _entry;
|
||||
|
||||
typedef struct LiteralDictionary {
|
||||
_entry* entries;
|
||||
int capacity;
|
||||
int count;
|
||||
int contains; //count + tombstones, for internal use
|
||||
} LiteralDictionary;
|
||||
|
||||
TOY_API void initLiteralDictionary(LiteralDictionary* dictionary);
|
||||
TOY_API void freeLiteralDictionary(LiteralDictionary* dictionary);
|
||||
|
||||
TOY_API void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
|
||||
TOY_API Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
||||
TOY_API void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
||||
|
||||
TOY_API bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "memory.h"
|
||||
#include "refstring.h"
|
||||
|
||||
#include "console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//default allocator
|
||||
void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
if (newSize == 0 && oldSize == 0) {
|
||||
//causes issues, so just skip out with a NO-OP
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (newSize == 0) {
|
||||
free(pointer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* mem = realloc(pointer, newSize);
|
||||
|
||||
if (mem == NULL) {
|
||||
fprintf(stderr, ERROR "[internal] Memory allocation error (requested %d for %ld, replacing %d)\n" RESET, (int)newSize, (long int)pointer, (int)oldSize);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
//static variables
|
||||
static MemoryAllocatorFn allocator;
|
||||
|
||||
//preload
|
||||
static void __attribute__((constructor)) preloadMemoryAllocator() {
|
||||
setMemoryAllocator(defaultMemoryAllocator);
|
||||
}
|
||||
|
||||
//exposed API
|
||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||
return allocator(pointer, oldSize, newSize);
|
||||
}
|
||||
|
||||
void setMemoryAllocator(MemoryAllocatorFn fn) {
|
||||
if (fn == NULL) {
|
||||
fprintf(stderr, ERROR "[internal] Memory allocator error (can't be null)\n" RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fn == reallocate) {
|
||||
fprintf(stderr, ERROR "[internal] Memory allocator error (can't loop the reallocate function)\n" RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
allocator = fn;
|
||||
setRefStringAllocatorFn(fn);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
|
||||
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
|
||||
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||
#define GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
||||
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||
|
||||
//implementation details
|
||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||
|
||||
//assign the memory allocator
|
||||
typedef void* (*MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void setMemoryAllocator(MemoryAllocatorFn);
|
||||
@@ -1,84 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum Opcode {
|
||||
OP_EOF,
|
||||
|
||||
//basic statements
|
||||
OP_ASSERT,
|
||||
OP_PRINT,
|
||||
|
||||
//data
|
||||
OP_LITERAL,
|
||||
OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
||||
OP_LITERAL_RAW, //forcibly get the raw value of the literal
|
||||
|
||||
//arithmetic operators
|
||||
OP_NEGATE,
|
||||
OP_ADDITION,
|
||||
OP_SUBTRACTION,
|
||||
OP_MULTIPLICATION,
|
||||
OP_DIVISION,
|
||||
OP_MODULO,
|
||||
OP_GROUPING_BEGIN,
|
||||
OP_GROUPING_END,
|
||||
|
||||
//variable stuff
|
||||
OP_SCOPE_BEGIN,
|
||||
OP_SCOPE_END,
|
||||
|
||||
OP_TYPE_DECL, //declare a type to be used (as a literal)
|
||||
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
||||
|
||||
OP_VAR_DECL, //declare a variable to be used (as a literal)
|
||||
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
||||
|
||||
OP_FN_DECL, //declare a function to be used (as a literal)
|
||||
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
|
||||
|
||||
OP_VAR_ASSIGN, //assign to a literal
|
||||
OP_VAR_ADDITION_ASSIGN,
|
||||
OP_VAR_SUBTRACTION_ASSIGN,
|
||||
OP_VAR_MULTIPLICATION_ASSIGN,
|
||||
OP_VAR_DIVISION_ASSIGN,
|
||||
OP_VAR_MODULO_ASSIGN,
|
||||
|
||||
OP_TYPE_CAST, //temporarily change a type of an atomic value
|
||||
OP_TYPE_OF, //get the type of a variable
|
||||
|
||||
OP_IMPORT,
|
||||
OP_EXPORT,
|
||||
|
||||
//for indexing
|
||||
OP_INDEX,
|
||||
OP_INDEX_ASSIGN,
|
||||
OP_INDEX_ASSIGN_INTERMEDIATE,
|
||||
OP_DOT,
|
||||
|
||||
//comparison of values
|
||||
OP_COMPARE_EQUAL,
|
||||
OP_COMPARE_NOT_EQUAL,
|
||||
OP_COMPARE_LESS,
|
||||
OP_COMPARE_LESS_EQUAL,
|
||||
OP_COMPARE_GREATER,
|
||||
OP_COMPARE_GREATER_EQUAL,
|
||||
OP_INVERT, //for booleans
|
||||
|
||||
//logical operators
|
||||
OP_AND,
|
||||
OP_OR,
|
||||
|
||||
//jumps, and conditional jumps (absolute)
|
||||
OP_JUMP,
|
||||
OP_IF_FALSE_JUMP,
|
||||
OP_FN_CALL,
|
||||
OP_FN_RETURN,
|
||||
|
||||
//pop the stack at the end of a complex statement
|
||||
OP_POP_STACK,
|
||||
|
||||
//meta
|
||||
OP_FN_END, //different from SECTION_END
|
||||
OP_SECTION_END = 255,
|
||||
//TODO: add more
|
||||
} Opcode;
|
||||
|
||||
-1773
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "lexer.h"
|
||||
#include "ast_node.h"
|
||||
|
||||
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
|
||||
typedef struct {
|
||||
Lexer* lexer;
|
||||
bool error; //I've had an error
|
||||
bool panic; //I am processing an error
|
||||
|
||||
//track the last two outputs from the lexer
|
||||
Token current;
|
||||
Token previous;
|
||||
} Parser;
|
||||
|
||||
TOY_API void initParser(Parser* parser, Lexer* lexer);
|
||||
TOY_API void freeParser(Parser* parser);
|
||||
TOY_API ASTNode* scanParser(Parser* parser);
|
||||
@@ -1,100 +0,0 @@
|
||||
#include "refstring.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//test variable sizes based on platform (safety)
|
||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||
|
||||
STATIC_ASSERT(sizeof(RefString) == 12);
|
||||
STATIC_ASSERT(sizeof(int) == 4);
|
||||
STATIC_ASSERT(sizeof(char) == 1);
|
||||
|
||||
//memory allocation
|
||||
static RefStringAllocatorFn allocate;
|
||||
|
||||
void setRefStringAllocatorFn(RefStringAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
RefString* createRefString(char* cstring) {
|
||||
int length = strlen(cstring);
|
||||
|
||||
return createRefStringLength(cstring, length);
|
||||
}
|
||||
|
||||
RefString* createRefStringLength(char* cstring, int length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
RefString* refString = (RefString*)allocate(NULL, 0, sizeof(int) * 2 + sizeof(char) * length + 1);
|
||||
|
||||
//set the data
|
||||
refString->refcount = 1;
|
||||
refString->length = length;
|
||||
strncpy(refString->data, cstring, refString->length);
|
||||
|
||||
refString->data[refString->length] = '\0'; //string terminator
|
||||
|
||||
return refString;
|
||||
}
|
||||
|
||||
void deleteRefString(RefString* refString) {
|
||||
if (refString->refcount > 0) {
|
||||
//decrement, then check
|
||||
refString->refcount--;
|
||||
if (refString->refcount <= 0) {
|
||||
allocate(refString, sizeof(int) * 2 + sizeof(char) * refString->length + 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int countRefString(RefString* refString) {
|
||||
return refString->refcount;
|
||||
}
|
||||
|
||||
int lengthRefString(RefString* refString) {
|
||||
return refString->length;
|
||||
}
|
||||
|
||||
RefString* copyRefString(RefString* refString) {
|
||||
//Cheaty McCheater Face
|
||||
refString->refcount++;
|
||||
return refString;
|
||||
}
|
||||
|
||||
RefString* deepCopyRefString(RefString* refString) {
|
||||
//create a new string, with a new refcount
|
||||
return createRefStringLength(refString->data, refString->length);
|
||||
}
|
||||
|
||||
char* toCString(RefString* refString) {
|
||||
return refString->data;
|
||||
}
|
||||
|
||||
bool equalsRefString(RefString* lhs, RefString* rhs) {
|
||||
//same pointer
|
||||
if (lhs == rhs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//different length
|
||||
if (lhs->length != rhs->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
|
||||
}
|
||||
|
||||
bool equalsRefStringCString(RefString* lhs, char* cstring) {
|
||||
//get the rhs length
|
||||
int length = strlen(cstring);
|
||||
|
||||
//different length
|
||||
if (lhs->length != length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, cstring, lhs->length) == 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//memory allocation hook
|
||||
typedef void* (*RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
void setRefStringAllocatorFn(RefStringAllocatorFn);
|
||||
|
||||
//the RefString structure
|
||||
typedef struct RefString {
|
||||
int refcount;
|
||||
int length;
|
||||
char data[1];
|
||||
} RefString;
|
||||
|
||||
//API
|
||||
RefString* createRefString(char* cstring);
|
||||
RefString* createRefStringLength(char* cstring, int length);
|
||||
void deleteRefString(RefString* refString);
|
||||
int countRefString(RefString* refString);
|
||||
int lengthRefString(RefString* refString);
|
||||
RefString* copyRefString(RefString* refString);
|
||||
RefString* deepCopyRefString(RefString* refString);
|
||||
char* toCString(RefString* refString);
|
||||
bool equalsRefString(RefString* lhs, RefString* rhs);
|
||||
bool equalsRefStringCString(RefString* lhs, char* cstring);
|
||||
-303
@@ -1,303 +0,0 @@
|
||||
#include "scope.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
//run up the ancestor chain, freeing anything with 0 references left
|
||||
static void freeAncestorChain(Scope* scope) {
|
||||
scope->references--;
|
||||
|
||||
//free scope chain
|
||||
if (scope->ancestor != NULL) {
|
||||
freeAncestorChain(scope->ancestor);
|
||||
}
|
||||
|
||||
if (scope->references > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
freeLiteralDictionary(&scope->variables);
|
||||
freeLiteralDictionary(&scope->types);
|
||||
|
||||
FREE(Scope, scope);
|
||||
}
|
||||
|
||||
//return false if invalid type
|
||||
static bool checkType(Literal typeLiteral, Literal original, Literal value, bool constCheck) {
|
||||
//for constants, fail if original != value
|
||||
if (constCheck && AS_TYPE(typeLiteral).constant && !literalsAreEqual(original, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//for any types
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//don't allow null types
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//always allow null values
|
||||
if (IS_NULL(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//for each type, if a mismatch is found, return false
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY && !IS_ARRAY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IS_ARRAY(value)) {
|
||||
//check value's type
|
||||
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new entry
|
||||
if (IS_NULL(original)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//check children
|
||||
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
|
||||
if (AS_ARRAY(original)->count <= i) {
|
||||
return true; //assume new entry pushed
|
||||
}
|
||||
|
||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(original)->literals[i], AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY && !IS_DICTIONARY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IS_DICTIONARY(value)) {
|
||||
//check value's type
|
||||
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new entry to a parent
|
||||
if (IS_NULL(original)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//check each child of value against the child of original
|
||||
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
|
||||
if (IS_NULL(AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
|
||||
continue;
|
||||
}
|
||||
|
||||
//find the internal child of original that matches this child of value
|
||||
_entry* ptr = NULL;
|
||||
|
||||
for (int j = 0; j < AS_DICTIONARY(original)->capacity; j++) {
|
||||
if (literalsAreEqual(AS_DICTIONARY(original)->entries[j].key, AS_DICTIONARY(value)->entries[i].key)) {
|
||||
ptr = &AS_DICTIONARY(original)->entries[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if not found, assume it's a new entry
|
||||
if (!ptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//check the type of key and value
|
||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], ptr->key, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], ptr->value, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FUNCTION && !IS_FUNCTION(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
Scope* pushScope(Scope* ancestor) {
|
||||
Scope* scope = ALLOCATE(Scope, 1);
|
||||
scope->ancestor = ancestor;
|
||||
initLiteralDictionary(&scope->variables);
|
||||
initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
Scope* popScope(Scope* scope) {
|
||||
if (scope == NULL) { //CAN pop a null
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Scope* ret = scope->ancestor;
|
||||
|
||||
//BUGFIX: when freeing a scope, free the function's scopes manually
|
||||
for (int i = 0; i < scope->variables.capacity; i++) {
|
||||
//handle keys, just in case
|
||||
if (IS_FUNCTION(scope->variables.entries[i].key)) {
|
||||
popScope(AS_FUNCTION(scope->variables.entries[i].key).scope);
|
||||
AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
|
||||
}
|
||||
|
||||
if (IS_FUNCTION(scope->variables.entries[i].value)) {
|
||||
popScope(AS_FUNCTION(scope->variables.entries[i].value).scope);
|
||||
AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
freeAncestorChain(scope);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Scope* copyScope(Scope* original) {
|
||||
Scope* scope = ALLOCATE(Scope, 1);
|
||||
scope->ancestor = original->ancestor;
|
||||
initLiteralDictionary(&scope->variables);
|
||||
initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
//copy the contents of the dictionaries
|
||||
for (int i = 0; i < original->variables.capacity; i++) {
|
||||
if (!IS_NULL(original->variables.entries[i].key)) {
|
||||
setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < original->types.capacity; i++) {
|
||||
if (!IS_NULL(original->types.entries[i].key)) {
|
||||
setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
//returns false if error
|
||||
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
|
||||
//don't redefine a variable within this scope
|
||||
if (existsLiteralDictionary(&scope->variables, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//store the type, for later checking on assignment
|
||||
setLiteralDictionary(&scope->types, key, type);
|
||||
|
||||
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!existsLiteralDictionary(&scope->variables, key)) {
|
||||
return isDelcaredScopeVariable(scope->ancestor, key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//return false if undefined, or can't be assigned
|
||||
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!existsLiteralDictionary(&scope->variables, key)) {
|
||||
return setScopeVariable(scope->ancestor, key, value, constCheck);
|
||||
}
|
||||
|
||||
//type checking
|
||||
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
|
||||
Literal original = getLiteralDictionary(&scope->variables, key);
|
||||
|
||||
if (!checkType(typeLiteral, original, value, constCheck)) {
|
||||
freeLiteral(typeLiteral);
|
||||
freeLiteral(original);
|
||||
return false;
|
||||
}
|
||||
|
||||
//actually assign
|
||||
setLiteralDictionary(&scope->variables, key, value);
|
||||
|
||||
freeLiteral(typeLiteral);
|
||||
freeLiteral(original);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!existsLiteralDictionary(&scope->variables, key)) {
|
||||
return getScopeVariable(scope->ancestor, key, valueHandle);
|
||||
}
|
||||
|
||||
*valueHandle = getLiteralDictionary(&scope->variables, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
Literal getScopeType(Scope* scope, Literal key) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!existsLiteralDictionary(&scope->types, key)) {
|
||||
return getScopeType(scope->ancestor, key);
|
||||
}
|
||||
|
||||
return getLiteralDictionary(&scope->types, key);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "literal_array.h"
|
||||
#include "literal_dictionary.h"
|
||||
|
||||
typedef struct Scope {
|
||||
LiteralDictionary variables; //only allow identifiers as the keys
|
||||
LiteralDictionary types; //the types, indexed by identifiers
|
||||
struct Scope* ancestor;
|
||||
int references; //how many scopes point here
|
||||
} Scope;
|
||||
|
||||
Scope* pushScope(Scope* scope);
|
||||
Scope* popScope(Scope* scope);
|
||||
Scope* copyScope(Scope* original);
|
||||
|
||||
//returns false if error
|
||||
bool declareScopeVariable(Scope* scope, Literal key, Literal type);
|
||||
bool isDelcaredScopeVariable(Scope* scope, Literal key);
|
||||
|
||||
//return false if undefined
|
||||
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
|
||||
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
|
||||
|
||||
Literal getScopeType(Scope* scope, Literal key);
|
||||
@@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum TokenType {
|
||||
//types
|
||||
TOKEN_NULL,
|
||||
TOKEN_BOOLEAN,
|
||||
TOKEN_INTEGER,
|
||||
TOKEN_FLOAT,
|
||||
TOKEN_STRING,
|
||||
TOKEN_ARRAY,
|
||||
TOKEN_DICTIONARY,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_OPAQUE,
|
||||
TOKEN_ANY,
|
||||
|
||||
//keywords and reserved words
|
||||
TOKEN_AS,
|
||||
TOKEN_ASSERT,
|
||||
TOKEN_BREAK,
|
||||
TOKEN_CLASS,
|
||||
TOKEN_CONST,
|
||||
TOKEN_CONTINUE,
|
||||
TOKEN_DO,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_EXPORT,
|
||||
TOKEN_FOR,
|
||||
TOKEN_FOREACH,
|
||||
TOKEN_IF,
|
||||
TOKEN_IMPORT,
|
||||
TOKEN_IN,
|
||||
TOKEN_OF,
|
||||
TOKEN_PRINT,
|
||||
TOKEN_RETURN,
|
||||
TOKEN_TYPE,
|
||||
TOKEN_ASTYPE,
|
||||
TOKEN_TYPEOF,
|
||||
TOKEN_VAR,
|
||||
TOKEN_WHILE,
|
||||
|
||||
//literal values
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_LITERAL_TRUE,
|
||||
TOKEN_LITERAL_FALSE,
|
||||
TOKEN_LITERAL_INTEGER,
|
||||
TOKEN_LITERAL_FLOAT,
|
||||
TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
TOKEN_PLUS,
|
||||
TOKEN_MINUS,
|
||||
TOKEN_MULTIPLY,
|
||||
TOKEN_DIVIDE,
|
||||
TOKEN_MODULO,
|
||||
TOKEN_PLUS_ASSIGN,
|
||||
TOKEN_MINUS_ASSIGN,
|
||||
TOKEN_MULTIPLY_ASSIGN,
|
||||
TOKEN_DIVIDE_ASSIGN,
|
||||
TOKEN_MODULO_ASSIGN,
|
||||
TOKEN_PLUS_PLUS,
|
||||
TOKEN_MINUS_MINUS,
|
||||
TOKEN_ASSIGN,
|
||||
|
||||
//logical operators
|
||||
TOKEN_PAREN_LEFT,
|
||||
TOKEN_PAREN_RIGHT,
|
||||
TOKEN_BRACKET_LEFT,
|
||||
TOKEN_BRACKET_RIGHT,
|
||||
TOKEN_BRACE_LEFT,
|
||||
TOKEN_BRACE_RIGHT,
|
||||
TOKEN_NOT,
|
||||
TOKEN_NOT_EQUAL,
|
||||
TOKEN_EQUAL,
|
||||
TOKEN_LESS,
|
||||
TOKEN_GREATER,
|
||||
TOKEN_LESS_EQUAL,
|
||||
TOKEN_GREATER_EQUAL,
|
||||
TOKEN_AND,
|
||||
TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOKEN_COLON,
|
||||
TOKEN_SEMICOLON,
|
||||
TOKEN_COMMA,
|
||||
TOKEN_DOT,
|
||||
TOKEN_PIPE,
|
||||
TOKEN_REST,
|
||||
|
||||
//meta tokens
|
||||
TOKEN_PASS,
|
||||
TOKEN_ERROR,
|
||||
TOKEN_EOF,
|
||||
} TokenType;
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
/* toy.h - A Toy Programming Language
|
||||
|
||||
If you're looking how to use Toy directly, try https://toylang.com/
|
||||
Otherwise, these headers may help learn how Toy works internally.
|
||||
|
||||
*/
|
||||
|
||||
/* utilities - these define a bunch of useful macros based on platform.
|
||||
|
||||
The most important one is `TOY_API`, which highlights functions intended for the end user.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_console_colors.h"
|
||||
#include "toy_memory.h"
|
||||
#include "toy_drive_system.h"
|
||||
|
||||
/* core pipeline - from source to execution
|
||||
|
||||
Each step is as follows:
|
||||
|
||||
source -> lexer -> token
|
||||
token -> parser -> AST
|
||||
AST -> compiler -> bytecode
|
||||
bytecode -> interpreter -> result
|
||||
|
||||
I should note that the parser -> compiler phase is actually made up of two steps - the write step
|
||||
and the collate step. See `Toy_compileString()` in `repl/repl_tools.c` for an example of how to compile
|
||||
properly.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_compiler.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
/* building block structures - the basic units of operation
|
||||
|
||||
Literals represent any value within the language, including some internal ones that you never see.
|
||||
Literal Arrays are literally arrays within memory, and are the most heavily used structure in Toy.
|
||||
Literal Dictionaries are unordered key-value hashmaps, that use a running strategy for collisions.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
/* other components - you probably won't use these directly, but they're a good learning opportunity.
|
||||
|
||||
`Toy_Scope` holds the variables of a specific scope within Toy - be it a script, a function, a block, etc.
|
||||
Scopes are also where the type system lives at runtime. They use identifier literals as keys, exclusively.
|
||||
|
||||
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and
|
||||
faster to copy and move. In reality, since strings are considered immutable, multiple variables can point
|
||||
to the same string to save memory, and you can just create a new one of these vars pointing to the original
|
||||
rather than copying entirely for a speed boost. This module has it's own memory allocator system that is
|
||||
plugged into the main memory allocator.
|
||||
|
||||
`Toy_RefFunction` acts similarly to `Toy_RefString`, but instead operates on function bytecode.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_scope.h"
|
||||
#include "toy_refstring.h"
|
||||
#include "toy_reffunction.h"
|
||||
@@ -0,0 +1,403 @@
|
||||
#include "toy_ast_node.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
|
||||
//don't free a NULL node
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(node->type) {
|
||||
case TOY_AST_NODE_ERROR:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_LITERAL:
|
||||
Toy_freeLiteral(node->atomic.literal);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_UNARY:
|
||||
Toy_freeASTNode(node->unary.child);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BINARY:
|
||||
Toy_freeASTNode(node->binary.left);
|
||||
Toy_freeASTNode(node->binary.right);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_TERNARY:
|
||||
Toy_freeASTNode(node->ternary.condition);
|
||||
Toy_freeASTNode(node->ternary.thenPath);
|
||||
Toy_freeASTNode(node->ternary.elsePath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_GROUPING:
|
||||
Toy_freeASTNode(node->grouping.child);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BLOCK:
|
||||
if (node->block.capacity > 0) {
|
||||
for (int i = 0; i < node->block.count; i++) {
|
||||
freeASTNodeCustom(node->block.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_COMPOUND:
|
||||
if (node->compound.capacity > 0) {
|
||||
for (int i = 0; i < node->compound.count; i++) {
|
||||
freeASTNodeCustom(node->compound.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PAIR:
|
||||
Toy_freeASTNode(node->pair.left);
|
||||
Toy_freeASTNode(node->pair.right);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_INDEX:
|
||||
Toy_freeASTNode(node->index.first);
|
||||
Toy_freeASTNode(node->index.second);
|
||||
Toy_freeASTNode(node->index.third);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_VAR_DECL:
|
||||
Toy_freeLiteral(node->varDecl.identifier);
|
||||
Toy_freeLiteral(node->varDecl.typeLiteral);
|
||||
Toy_freeASTNode(node->varDecl.expression);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_COLLECTION:
|
||||
if (node->fnCollection.capacity > 0) {
|
||||
for (int i = 0; i < node->fnCollection.count; i++) {
|
||||
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_DECL:
|
||||
Toy_freeLiteral(node->fnDecl.identifier);
|
||||
Toy_freeASTNode(node->fnDecl.arguments);
|
||||
Toy_freeASTNode(node->fnDecl.returns);
|
||||
Toy_freeASTNode(node->fnDecl.block);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_CALL:
|
||||
Toy_freeASTNode(node->fnCall.arguments);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_RETURN:
|
||||
Toy_freeASTNode(node->returns.returns);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_IF:
|
||||
Toy_freeASTNode(node->pathIf.condition);
|
||||
Toy_freeASTNode(node->pathIf.thenPath);
|
||||
Toy_freeASTNode(node->pathIf.elsePath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_WHILE:
|
||||
Toy_freeASTNode(node->pathWhile.condition);
|
||||
Toy_freeASTNode(node->pathWhile.thenPath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FOR:
|
||||
Toy_freeASTNode(node->pathFor.preClause);
|
||||
Toy_freeASTNode(node->pathFor.postClause);
|
||||
Toy_freeASTNode(node->pathFor.condition);
|
||||
Toy_freeASTNode(node->pathFor.thenPath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BREAK:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_CONTINUE:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PREFIX_INCREMENT:
|
||||
Toy_freeLiteral(node->prefixIncrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_PREFIX_DECREMENT:
|
||||
Toy_freeLiteral(node->prefixDecrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_POSTFIX_INCREMENT:
|
||||
Toy_freeLiteral(node->postfixIncrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_POSTFIX_DECREMENT:
|
||||
Toy_freeLiteral(node->postfixDecrement.identifier);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_IMPORT:
|
||||
Toy_freeLiteral(node->import.identifier);
|
||||
Toy_freeLiteral(node->import.alias);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PASS:
|
||||
//EMPTY
|
||||
break;
|
||||
}
|
||||
|
||||
if (freeSelf) {
|
||||
TOY_FREE(Toy_ASTNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_freeASTNode(Toy_ASTNode* node) {
|
||||
freeASTNodeCustom(node, true);
|
||||
}
|
||||
|
||||
//various emitters
|
||||
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal) {
|
||||
//allocate a new node
|
||||
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = TOY_AST_NODE_LITERAL;
|
||||
(*nodeHandle)->atomic.literal = Toy_copyLiteral(literal);
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child) {
|
||||
//allocate a new node
|
||||
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = TOY_AST_NODE_UNARY;
|
||||
(*nodeHandle)->unary.opcode = opcode;
|
||||
(*nodeHandle)->unary.child = child;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BINARY;
|
||||
tmp->binary.opcode = opcode;
|
||||
tmp->binary.left = *nodeHandle;
|
||||
tmp->binary.right = rhs;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_TERNARY;
|
||||
tmp->ternary.condition = condition;
|
||||
tmp->ternary.thenPath = thenPath;
|
||||
tmp->ternary.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_GROUPING;
|
||||
tmp->grouping.child = *nodeHandle;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BLOCK;
|
||||
tmp->block.nodes = NULL; //NOTE: appended by the parser
|
||||
tmp->block.capacity = 0;
|
||||
tmp->block.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_COMPOUND;
|
||||
tmp->compound.literalType = literalType;
|
||||
tmp->compound.nodes = NULL;
|
||||
tmp->compound.capacity = 0;
|
||||
tmp->compound.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right) {
|
||||
//set - assume the node has already been allocated
|
||||
node->type = TOY_AST_NODE_PAIR;
|
||||
node->pair.left = left;
|
||||
node->pair.right = right;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_INDEX;
|
||||
tmp->index.first = first;
|
||||
tmp->index.second = second;
|
||||
tmp->index.third = third;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal typeLiteral, Toy_ASTNode* expression) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_VAR_DECL;
|
||||
tmp->varDecl.identifier = identifier;
|
||||
tmp->varDecl.typeLiteral = typeLiteral;
|
||||
tmp->varDecl.expression = expression;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_COLLECTION;
|
||||
tmp->fnCollection.nodes = NULL;
|
||||
tmp->fnCollection.capacity = 0;
|
||||
tmp->fnCollection.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_DECL;
|
||||
tmp->fnDecl.identifier = identifier;
|
||||
tmp->fnDecl.arguments = arguments;
|
||||
tmp->fnDecl.returns = returns;
|
||||
tmp->fnDecl.block = block;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_CALL;
|
||||
tmp->fnCall.arguments = arguments;
|
||||
tmp->fnCall.argumentCount = arguments->fnCollection.count;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_RETURN;
|
||||
tmp->returns.returns = returns;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_IF;
|
||||
tmp->pathIf.condition = condition;
|
||||
tmp->pathIf.thenPath = thenPath;
|
||||
tmp->pathIf.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_WHILE;
|
||||
tmp->pathWhile.condition = condition;
|
||||
tmp->pathWhile.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FOR;
|
||||
tmp->pathFor.preClause = preClause;
|
||||
tmp->pathFor.condition = condition;
|
||||
tmp->pathFor.postClause = postClause;
|
||||
tmp->pathFor.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BREAK;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_CONTINUE;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PREFIX_INCREMENT;
|
||||
tmp->prefixIncrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PREFIX_DECREMENT;
|
||||
tmp->prefixDecrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_POSTFIX_INCREMENT;
|
||||
tmp->postfixIncrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_POSTFIX_DECREMENT;
|
||||
tmp->postfixDecrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_IMPORT;
|
||||
tmp->import.identifier = Toy_copyLiteral(identifier);
|
||||
tmp->import.alias = Toy_copyLiteral(alias);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePass(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PASS;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_literal.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_token_types.h"
|
||||
|
||||
//nodes are the intermediaries between parsers and compilers
|
||||
typedef union Toy_private_node Toy_ASTNode;
|
||||
|
||||
typedef enum Toy_ASTNodeType {
|
||||
TOY_AST_NODE_ERROR,
|
||||
TOY_AST_NODE_LITERAL, //a simple value
|
||||
TOY_AST_NODE_UNARY, //one child + opcode
|
||||
TOY_AST_NODE_BINARY, //two children, left and right + opcode
|
||||
TOY_AST_NODE_TERNARY, //three children, condition, then path & else path
|
||||
TOY_AST_NODE_GROUPING, //one child
|
||||
TOY_AST_NODE_BLOCK, //contains a sub-node array
|
||||
TOY_AST_NODE_COMPOUND, //contains a sub-node array
|
||||
TOY_AST_NODE_PAIR, //contains a left and right
|
||||
TOY_AST_NODE_INDEX, //index a variable
|
||||
TOY_AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
|
||||
TOY_AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
|
||||
TOY_AST_NODE_FN_COLLECTION, //parts of a function
|
||||
TOY_AST_NODE_FN_CALL, //call a function
|
||||
TOY_AST_NODE_FN_RETURN, //for control flow
|
||||
TOY_AST_NODE_IF, //for control flow
|
||||
TOY_AST_NODE_WHILE, //for control flow
|
||||
TOY_AST_NODE_FOR, //for control flow
|
||||
TOY_AST_NODE_BREAK, //for control flow
|
||||
TOY_AST_NODE_CONTINUE, //for control flow
|
||||
TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable
|
||||
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||
TOY_AST_NODE_POSTFIX_DECREMENT, //decrement a variable
|
||||
TOY_AST_NODE_IMPORT, //import a library
|
||||
TOY_AST_NODE_PASS, //for doing nothing
|
||||
} Toy_ASTNodeType;
|
||||
|
||||
//literals
|
||||
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal);
|
||||
|
||||
typedef struct Toy_NodeLiteral {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal literal;
|
||||
} Toy_NodeLiteral;
|
||||
|
||||
//unary operator
|
||||
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child);
|
||||
|
||||
typedef struct Toy_NodeUnary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Opcode opcode;
|
||||
Toy_ASTNode* child;
|
||||
} Toy_NodeUnary;
|
||||
|
||||
//binary operator
|
||||
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode); //handled node becomes lhs
|
||||
|
||||
typedef struct Toy_NodeBinary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Opcode opcode;
|
||||
Toy_ASTNode* left;
|
||||
Toy_ASTNode* right;
|
||||
} Toy_NodeBinary;
|
||||
|
||||
//ternary operator
|
||||
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
|
||||
|
||||
typedef struct Toy_NodeTernary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
Toy_ASTNode* elsePath;
|
||||
} Toy_NodeTernary;
|
||||
|
||||
//grouping of other AST nodes
|
||||
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeGrouping {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* child;
|
||||
} Toy_NodeGrouping;
|
||||
|
||||
//block of statement nodes
|
||||
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeBlock {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeBlock;
|
||||
|
||||
//compound literals (array, dictionary)
|
||||
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType);
|
||||
|
||||
typedef struct Toy_NodeCompound {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_LiteralType literalType;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeCompound;
|
||||
|
||||
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right); //NOTE: this is a set function, not an emit function
|
||||
|
||||
typedef struct Toy_NodePair {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* left;
|
||||
Toy_ASTNode* right;
|
||||
} Toy_NodePair;
|
||||
|
||||
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third);
|
||||
|
||||
typedef struct Toy_NodeIndex {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* first;
|
||||
Toy_ASTNode* second;
|
||||
Toy_ASTNode* third;
|
||||
} Toy_NodeIndex;
|
||||
|
||||
//variable declaration
|
||||
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal type, Toy_ASTNode* expression);
|
||||
|
||||
typedef struct Toy_NodeVarDecl {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_Literal typeLiteral;
|
||||
Toy_ASTNode* expression;
|
||||
} Toy_NodeVarDecl;
|
||||
|
||||
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
|
||||
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeFnCollection {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeFnCollection;
|
||||
|
||||
//function declaration
|
||||
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block);
|
||||
|
||||
typedef struct Toy_NodeFnDecl {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_ASTNode* arguments;
|
||||
Toy_ASTNode* returns;
|
||||
Toy_ASTNode* block;
|
||||
} Toy_NodeFnDecl;
|
||||
|
||||
//function call
|
||||
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments);
|
||||
|
||||
typedef struct Toy_NodeFnCall {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* arguments;
|
||||
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
|
||||
} Toy_NodeFnCall;
|
||||
|
||||
//function return
|
||||
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns);
|
||||
|
||||
typedef struct Toy_NodeFnReturn {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* returns;
|
||||
} Toy_NodeFnReturn;
|
||||
|
||||
//control flow path - if-else, while, for, break, continue, return
|
||||
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
|
||||
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath);
|
||||
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath);
|
||||
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle);
|
||||
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeIf {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
Toy_ASTNode* elsePath;
|
||||
} Toy_NodeIf;
|
||||
|
||||
typedef struct Toy_NodeWhile {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
} Toy_NodeWhile;
|
||||
|
||||
typedef struct Toy_NodeFor {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* preClause;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* postClause;
|
||||
Toy_ASTNode* thenPath;
|
||||
} Toy_NodeFor;
|
||||
|
||||
typedef struct Toy_NodeBreak {
|
||||
Toy_ASTNodeType type;
|
||||
} Toy_NodeBreak;
|
||||
|
||||
typedef struct Toy_NodeContinue {
|
||||
Toy_ASTNodeType type;
|
||||
} Toy_NodeContinue;
|
||||
|
||||
//pre-post increment/decrement
|
||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
|
||||
typedef struct Toy_NodePrefixIncrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePrefixIncrement;
|
||||
|
||||
typedef struct Toy_NodePrefixDecrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePrefixDecrement;
|
||||
|
||||
typedef struct Toy_NodePostfixIncrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePostfixIncrement;
|
||||
|
||||
typedef struct Toy_NodePostfixDecrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePostfixDecrement;
|
||||
|
||||
//import a library
|
||||
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
typedef struct Toy_NodeImport {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_Literal alias;
|
||||
} Toy_NodeImport;
|
||||
|
||||
//for doing nothing
|
||||
void Toy_emitASTNodePass(Toy_ASTNode** nodeHandle);
|
||||
|
||||
union Toy_private_node {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_NodeLiteral atomic;
|
||||
Toy_NodeUnary unary;
|
||||
Toy_NodeBinary binary;
|
||||
Toy_NodeTernary ternary;
|
||||
Toy_NodeGrouping grouping;
|
||||
Toy_NodeBlock block;
|
||||
Toy_NodeCompound compound;
|
||||
Toy_NodePair pair;
|
||||
Toy_NodeIndex index;
|
||||
Toy_NodeVarDecl varDecl;
|
||||
Toy_NodeFnCollection fnCollection;
|
||||
Toy_NodeFnDecl fnDecl;
|
||||
Toy_NodeFnCall fnCall;
|
||||
Toy_NodeFnReturn returns;
|
||||
Toy_NodeIf pathIf;
|
||||
Toy_NodeWhile pathWhile;
|
||||
Toy_NodeFor pathFor;
|
||||
Toy_NodeBreak pathBreak;
|
||||
Toy_NodeContinue pathContinue;
|
||||
Toy_NodePrefixIncrement prefixIncrement;
|
||||
Toy_NodePrefixDecrement prefixDecrement;
|
||||
Toy_NodePostfixIncrement postfixIncrement;
|
||||
Toy_NodePostfixDecrement postfixDecrement;
|
||||
Toy_NodeImport import;
|
||||
};
|
||||
|
||||
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
//the _index function is a historical oddity - it's used whenever a compound is indexed
|
||||
int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
|
||||
//globally available native functions
|
||||
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
+77
-48
@@ -4,7 +4,7 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//test variable sizes based on platform
|
||||
//test variable sizes based on platform - see issue #35
|
||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||
|
||||
STATIC_ASSERT(sizeof(char) == 1);
|
||||
@@ -15,77 +15,106 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
|
||||
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
||||
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
static const char* build = __DATE__ " " __TIME__;
|
||||
|
||||
//declare the singleton
|
||||
Command command;
|
||||
const char* Toy_private_version_build() {
|
||||
return build;
|
||||
}
|
||||
|
||||
void initCommand(int argc, const char* argv[]) {
|
||||
//default values
|
||||
command.error = false;
|
||||
command.help = false;
|
||||
command.version = false;
|
||||
command.binaryfile = NULL;
|
||||
command.sourcefile = NULL;
|
||||
command.compilefile = NULL;
|
||||
command.outfile = "out.tb";
|
||||
command.source = NULL;
|
||||
command.verbose = false;
|
||||
//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[]) {
|
||||
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||
command.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
|
||||
|
||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
command.help = true;
|
||||
command.error = false;
|
||||
Toy_commandLine.help = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||
command.version = true;
|
||||
command.error = false;
|
||||
Toy_commandLine.version = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
||||
command.verbose = true;
|
||||
command.error = false;
|
||||
Toy_commandLine.verbose = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
||||
command.sourcefile = (char*)argv[i + 1];
|
||||
Toy_commandLine.sourcefile = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
||||
command.source = (char*)argv[i + 1];
|
||||
Toy_commandLine.source = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
||||
command.compilefile = (char*)argv[i + 1];
|
||||
Toy_commandLine.compilefile = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
||||
command.outfile = (char*)argv[i + 1];
|
||||
Toy_commandLine.outfile = (char*)argv[i + 1];
|
||||
i++;
|
||||
command.error = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-t") || !strcmp(argv[i], "--initial")) && i + 1 < argc) {
|
||||
Toy_commandLine.initialfile = (char*)argv[i + 1];
|
||||
i++;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-p")) {
|
||||
Toy_commandLine.parseBytecodeHeader = true;
|
||||
|
||||
if (Toy_commandLine.binaryfile) {
|
||||
Toy_commandLine.error = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-n")) {
|
||||
Toy_commandLine.enablePrintNewline = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
//option without a flag + ending in .tb = binary input
|
||||
if (i < argc) {
|
||||
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
||||
command.binaryfile = (char*)argv[i];
|
||||
command.error = false;
|
||||
Toy_commandLine.binaryfile = (char*)argv[i];
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -95,31 +124,31 @@ void initCommand(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
void usageCommand(int argc, const char* argv[]) {
|
||||
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
|
||||
void Toy_usageCommandLine(int argc, const char* argv[]) {
|
||||
printf("Usage: %s [ file.tb | -h | -v | -d | -f file.toy | -i source | -c file.toy -o out.tb | -t file.toy ]\n\n", argv[0]);
|
||||
}
|
||||
|
||||
void helpCommand(int argc, const char* argv[]) {
|
||||
usageCommand(argc, argv);
|
||||
void Toy_helpCommandLine(int argc, const char* 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\t| --help\t\tShow this help then exit.\n\n");
|
||||
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
|
||||
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
|
||||
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
|
||||
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
|
||||
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
|
||||
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
|
||||
printf(" -h, --help\t\t\tShow this help then exit.\n");
|
||||
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
|
||||
printf(" -d, --debug\t\t\tBe verbose when operating.\n");
|
||||
printf(" -f, --file filename\t\tParse, compile and execute the source file.\n");
|
||||
printf(" -i, --input source\t\tParse, compile and execute this given string of source code.\n");
|
||||
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
|
||||
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
|
||||
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
|
||||
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
|
||||
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
|
||||
}
|
||||
|
||||
void copyrightCommand(int argc, const char* argv[]) {
|
||||
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
|
||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
|
||||
printf("Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios\n\n");
|
||||
printf("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.\n\n");
|
||||
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
|
||||
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
|
||||
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
|
||||
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+29
-27
@@ -4,31 +4,33 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 6
|
||||
#define TOY_VERSION_PATCH 4
|
||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||
#define TOY_VERSION_MAJOR 1
|
||||
#define TOY_VERSION_MINOR 1
|
||||
#define TOY_VERSION_PATCH 6
|
||||
#define TOY_VERSION_BUILD Toy_private_version_build()
|
||||
|
||||
//platform/compiler-specific instructions
|
||||
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||
|
||||
//platform exports/imports
|
||||
#if defined(__linux__)
|
||||
#define TOY_API extern
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
#define TOY_API
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
#define TOY_API __declspec(dllimport)
|
||||
#else
|
||||
#define TOY_API __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define TOY_API
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define TOY_API extern
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
//for processing the command line arguments
|
||||
TOY_API const char* Toy_private_version_build();
|
||||
|
||||
//for processing the command line arguments in the repl
|
||||
typedef struct {
|
||||
bool error;
|
||||
bool help;
|
||||
@@ -38,17 +40,17 @@ typedef struct {
|
||||
char* compilefile;
|
||||
char* outfile; //defaults to out.tb
|
||||
char* source;
|
||||
char* initialfile;
|
||||
bool enablePrintNewline;
|
||||
bool parseBytecodeHeader;
|
||||
bool verbose;
|
||||
} Command;
|
||||
} Toy_CommandLine;
|
||||
|
||||
extern Command command;
|
||||
//these are intended for the repl only, despite using the api prefix
|
||||
TOY_API Toy_CommandLine Toy_commandLine;
|
||||
|
||||
void initCommand(int argc, const char* argv[]);
|
||||
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
|
||||
|
||||
void usageCommand(int argc, const char* argv[]);
|
||||
void helpCommand(int argc, const char* argv[]);
|
||||
void copyrightCommand(int argc, const char* argv[]);
|
||||
#endif
|
||||
|
||||
//NOTE: assigning to a byte from a short loses data
|
||||
#define AS_USHORT(value) (*(unsigned short*)(&(value)))
|
||||
TOY_API void Toy_usageCommandLine(int argc, const char* argv[]);
|
||||
TOY_API void Toy_helpCommandLine(int argc, const char* argv[]);
|
||||
TOY_API void Toy_copyrightCommandLine(int argc, const char* argv[]);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_ast_node.h"
|
||||
#include "toy_literal_array.h"
|
||||
|
||||
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
||||
typedef struct Toy_Compiler {
|
||||
Toy_LiteralArray literalCache;
|
||||
unsigned char* bytecode;
|
||||
int capacity;
|
||||
int count;
|
||||
bool panic;
|
||||
} Toy_Compiler;
|
||||
|
||||
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
|
||||
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
|
||||
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
|
||||
|
||||
//embed the header, data section, code section, function section, etc.
|
||||
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size);
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
/* toy_console_colors.h - console utility
|
||||
|
||||
This file provides a number of macros that can set the color of text in a console
|
||||
window. These are used for convenience only. They are supposed to be dropped into
|
||||
a printf()'s first argument, like so:
|
||||
|
||||
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
|
||||
|
||||
NOTE: you need both font AND background for these to work
|
||||
|
||||
*/
|
||||
|
||||
//platform/compiler-specific instructions
|
||||
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||
|
||||
//fonts color
|
||||
#define TOY_CC_FONT_BLACK "\033[30;"
|
||||
#define TOY_CC_FONT_RED "\033[31;"
|
||||
#define TOY_CC_FONT_GREEN "\033[32;"
|
||||
#define TOY_CC_FONT_YELLOW "\033[33;"
|
||||
#define TOY_CC_FONT_BLUE "\033[34;"
|
||||
#define TOY_CC_FONT_PURPLE "\033[35;"
|
||||
#define TOY_CC_FONT_DGREEN "\033[6;"
|
||||
#define TOY_CC_FONT_WHITE "\033[7;"
|
||||
#define TOY_CC_FONT_CYAN "\x1b[36m"
|
||||
|
||||
//background color
|
||||
#define TOY_CC_BACK_BLACK "40m"
|
||||
#define TOY_CC_BACK_RED "41m"
|
||||
#define TOY_CC_BACK_GREEN "42m"
|
||||
#define TOY_CC_BACK_YELLOW "43m"
|
||||
#define TOY_CC_BACK_BLUE "44m"
|
||||
#define TOY_CC_BACK_PURPLE "45m"
|
||||
#define TOY_CC_BACK_DGREEN "46m"
|
||||
#define TOY_CC_BACK_WHITE "47m"
|
||||
|
||||
//useful
|
||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_RESET "\033[0m"
|
||||
|
||||
#else
|
||||
|
||||
//fonts color
|
||||
#define TOY_CC_FONT_BLACK
|
||||
#define TOY_CC_FONT_RED
|
||||
#define TOY_CC_FONT_GREEN
|
||||
#define TOY_CC_FONT_YELLOW
|
||||
#define TOY_CC_FONT_BLUE
|
||||
#define TOY_CC_FONT_PURPLE
|
||||
#define TOY_CC_FONT_DGREEN
|
||||
#define TOY_CC_FONT_WHITE
|
||||
#define TOY_CC_FONT_CYAN
|
||||
|
||||
//background color
|
||||
#define TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_BACK_RED
|
||||
#define TOY_CC_BACK_GREEN
|
||||
#define TOY_CC_BACK_YELLOW
|
||||
#define TOY_CC_BACK_BLUE
|
||||
#define TOY_CC_BACK_PURPLE
|
||||
#define TOY_CC_BACK_DGREEN
|
||||
#define TOY_CC_BACK_WHITE
|
||||
|
||||
//useful
|
||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_RESET
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "toy_drive_system.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//file system API
|
||||
static Toy_LiteralDictionary driveDictionary;
|
||||
|
||||
void Toy_initDriveSystem() {
|
||||
Toy_initLiteralDictionary(&driveDictionary);
|
||||
}
|
||||
|
||||
void Toy_freeDriveSystem() {
|
||||
Toy_freeLiteralDictionary(&driveDictionary);
|
||||
}
|
||||
|
||||
TOY_API void Toy_setDrivePath(char* drive, char* path) {
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
|
||||
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
|
||||
|
||||
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
|
||||
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
|
||||
//check argument types
|
||||
if (!TOY_IS_STRING(*drivePathLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
size_t driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
driveLength++;
|
||||
}
|
||||
|
||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||
|
||||
//get the real drive file path
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
|
||||
|
||||
if (!TOY_IS_STRING(pathLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\n");
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
Toy_deleteRefString(filePath);
|
||||
Toy_deleteRefString(drivePath);
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
|
||||
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
|
||||
|
||||
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
|
||||
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
|
||||
|
||||
//clean up the drive/path stuff
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_deleteRefString(filePath);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
|
||||
//check for break-out attempts
|
||||
for (size_t i = 0; i < fileLength - 1; i++) {
|
||||
if (file[i] == '.' && file[i + 1] == '.') {
|
||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
|
||||
|
||||
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
//file system API - these need to be set by the host
|
||||
TOY_API void Toy_initDriveSystem();
|
||||
TOY_API void Toy_freeDriveSystem();
|
||||
|
||||
//file system API - for use with libs
|
||||
TOY_API void Toy_setDrivePath(char* drive, char* path);
|
||||
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
//the interpreter acts depending on the bytecode instructions
|
||||
typedef struct Toy_Interpreter {
|
||||
//input
|
||||
const unsigned char* bytecode;
|
||||
int length;
|
||||
int count;
|
||||
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
||||
Toy_LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||
|
||||
//operation
|
||||
Toy_Scope* scope;
|
||||
Toy_LiteralArray stack;
|
||||
|
||||
//Library APIs
|
||||
Toy_LiteralDictionary* hooks;
|
||||
|
||||
//debug outputs
|
||||
Toy_PrintFn printOutput;
|
||||
Toy_PrintFn assertOutput;
|
||||
Toy_PrintFn errorOutput;
|
||||
|
||||
int depth; //don't overflow
|
||||
bool panic;
|
||||
} Toy_Interpreter;
|
||||
|
||||
//native API
|
||||
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func);
|
||||
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook);
|
||||
|
||||
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, 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);
|
||||
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
|
||||
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
|
||||
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length); //run the code
|
||||
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "toy_keyword_types.h"
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
Toy_KeywordType Toy_keywordTypes[] = {
|
||||
//type keywords
|
||||
{TOY_TOKEN_NULL, "null"},
|
||||
{TOY_TOKEN_BOOLEAN, "bool"},
|
||||
{TOY_TOKEN_INTEGER, "int"},
|
||||
{TOY_TOKEN_FLOAT, "float"},
|
||||
{TOY_TOKEN_STRING, "string"},
|
||||
{TOY_TOKEN_FUNCTION, "fn"},
|
||||
{TOY_TOKEN_OPAQUE, "opaque"},
|
||||
{TOY_TOKEN_ANY, "any"},
|
||||
|
||||
//other keywords
|
||||
{TOY_TOKEN_AS, "as"},
|
||||
{TOY_TOKEN_ASSERT, "assert"},
|
||||
{TOY_TOKEN_BREAK, "break"},
|
||||
{TOY_TOKEN_CLASS, "class"},
|
||||
{TOY_TOKEN_CONST, "const"},
|
||||
{TOY_TOKEN_CONTINUE, "continue"},
|
||||
{TOY_TOKEN_DO, "do"},
|
||||
{TOY_TOKEN_ELSE, "else"},
|
||||
{TOY_TOKEN_EXPORT, "export"},
|
||||
{TOY_TOKEN_FOR, "for"},
|
||||
{TOY_TOKEN_FOREACH, "foreach"},
|
||||
{TOY_TOKEN_IF, "if"},
|
||||
{TOY_TOKEN_IMPORT, "import"},
|
||||
{TOY_TOKEN_IN, "in"},
|
||||
{TOY_TOKEN_OF, "of"},
|
||||
{TOY_TOKEN_PRINT, "print"},
|
||||
{TOY_TOKEN_RETURN, "return"},
|
||||
{TOY_TOKEN_TYPE, "type"},
|
||||
{TOY_TOKEN_ASTYPE, "astype"},
|
||||
{TOY_TOKEN_TYPEOF, "typeof"},
|
||||
{TOY_TOKEN_VAR, "var"},
|
||||
{TOY_TOKEN_WHILE, "while"},
|
||||
|
||||
//literal values
|
||||
{TOY_TOKEN_LITERAL_TRUE, "true"},
|
||||
{TOY_TOKEN_LITERAL_FALSE, "false"},
|
||||
|
||||
//meta tokens
|
||||
{TOY_TOKEN_PASS, NULL},
|
||||
{TOY_TOKEN_ERROR, NULL},
|
||||
|
||||
{TOY_TOKEN_EOF, NULL},
|
||||
};
|
||||
|
||||
char* Toy_findKeywordByType(Toy_TokenType type) {
|
||||
if (type == TOY_TOKEN_EOF) {
|
||||
return "EOF";
|
||||
}
|
||||
|
||||
for(int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (Toy_keywordTypes[i].type == type) {
|
||||
return Toy_keywordTypes[i].keyword;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_TokenType Toy_findTypeByKeyword(const char* keyword) {
|
||||
const int length = strlen(keyword);
|
||||
|
||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (!strncmp(keyword, Toy_keywordTypes[i].keyword, length)) {
|
||||
return Toy_keywordTypes[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
return TOY_TOKEN_EOF;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_token_types.h"
|
||||
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
char* keyword;
|
||||
} Toy_KeywordType;
|
||||
|
||||
extern Toy_KeywordType Toy_keywordTypes[];
|
||||
|
||||
char* Toy_findKeywordByType(Toy_TokenType type);
|
||||
|
||||
Toy_TokenType Toy_findTypeByKeyword(const char* keyword);
|
||||
@@ -0,0 +1,383 @@
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_console_colors.h"
|
||||
#include "toy_keyword_types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//static generic utility functions
|
||||
static void cleanLexer(Toy_Lexer* lexer) {
|
||||
lexer->source = NULL;
|
||||
lexer->start = 0;
|
||||
lexer->current = 0;
|
||||
lexer->line = 1;
|
||||
lexer->commentsEnabled = true;
|
||||
}
|
||||
|
||||
static bool isAtEnd(Toy_Lexer* lexer) {
|
||||
return lexer->source[lexer->current] == '\0';
|
||||
}
|
||||
|
||||
static char peek(Toy_Lexer* lexer) {
|
||||
return lexer->source[lexer->current];
|
||||
}
|
||||
|
||||
static char peekNext(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) return '\0';
|
||||
return lexer->source[lexer->current + 1];
|
||||
}
|
||||
|
||||
static char advance(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
//new line
|
||||
if (lexer->source[lexer->current] == '\n') {
|
||||
lexer->line++;
|
||||
}
|
||||
|
||||
lexer->current++;
|
||||
return lexer->source[lexer->current - 1];
|
||||
}
|
||||
|
||||
static void eatWhitespace(Toy_Lexer* lexer) {
|
||||
const char c = peek(lexer);
|
||||
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
advance(lexer);
|
||||
break;
|
||||
|
||||
//comments
|
||||
case '/':
|
||||
if (!lexer->commentsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
//eat the line
|
||||
if (peekNext(lexer) == '/') {
|
||||
while (!isAtEnd(lexer) && advance(lexer) != '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
//eat the block
|
||||
if (peekNext(lexer) == '*') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
while(!isAtEnd(lexer) && !(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
//tail recursion
|
||||
eatWhitespace(lexer);
|
||||
}
|
||||
|
||||
static bool isDigit(Toy_Lexer* lexer) {
|
||||
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(Toy_Lexer* lexer) {
|
||||
return
|
||||
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
||||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
||||
peek(lexer) == '_'
|
||||
;
|
||||
}
|
||||
|
||||
static bool match(Toy_Lexer* lexer, char c) {
|
||||
if (peek(lexer) == c) {
|
||||
advance(lexer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//token generators
|
||||
static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_ERROR;
|
||||
token.lexeme = msg;
|
||||
token.length = strlen(msg);
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("err:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = type;
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.lexeme = &lexer->source[lexer->current - token.length];
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("tok:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
|
||||
|
||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||
|
||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
||||
type = TOY_TOKEN_LITERAL_FLOAT;
|
||||
advance(lexer);
|
||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
|
||||
token.type = type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
if (type == TOY_TOKEN_LITERAL_INTEGER) {
|
||||
printf("int:");
|
||||
} else {
|
||||
printf("flt:");
|
||||
}
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static bool isEscapableCharacter(char c) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
case 't':
|
||||
case '\\':
|
||||
case '"':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||
while (!isAtEnd(lexer)) {
|
||||
//stop if you've hit the terminator
|
||||
if (peek(lexer) == terminator) {
|
||||
advance(lexer); //eat terminator
|
||||
break;
|
||||
}
|
||||
|
||||
//skip escaped control characters
|
||||
if (peek(lexer) == '\\' && isEscapableCharacter(peekNext(lexer))) {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
continue;
|
||||
}
|
||||
|
||||
//otherwise
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
if (isAtEnd(lexer)) {
|
||||
return makeErrorToken(lexer, "Unterminated string");
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_LITERAL_STRING;
|
||||
token.lexeme = &lexer->source[lexer->start + 1];
|
||||
token.length = lexer->current - lexer->start - 2;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("str:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
advance(lexer); //first letter can only be alpha
|
||||
|
||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
//scan for a keyword
|
||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = Toy_keywordTypes[i].type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("kwd:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
//return an identifier
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_IDENTIFIER;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("idf:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
|
||||
cleanLexer(lexer);
|
||||
|
||||
lexer->source = source;
|
||||
}
|
||||
|
||||
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
||||
eatWhitespace(lexer);
|
||||
|
||||
lexer->start = lexer->current;
|
||||
|
||||
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
|
||||
|
||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||
|
||||
char c = advance(lexer);
|
||||
|
||||
switch(c) {
|
||||
case '(': return makeToken(lexer, TOY_TOKEN_PAREN_LEFT);
|
||||
case ')': return makeToken(lexer, TOY_TOKEN_PAREN_RIGHT);
|
||||
case '{': return makeToken(lexer, TOY_TOKEN_BRACE_LEFT);
|
||||
case '}': return makeToken(lexer, TOY_TOKEN_BRACE_RIGHT);
|
||||
case '[': return makeToken(lexer, TOY_TOKEN_BRACKET_LEFT);
|
||||
case ']': return makeToken(lexer, TOY_TOKEN_BRACKET_RIGHT);
|
||||
|
||||
case '+': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOY_TOKEN_PLUS_PLUS: TOY_TOKEN_PLUS);
|
||||
case '-': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOY_TOKEN_MINUS_MINUS: TOY_TOKEN_MINUS);
|
||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MULTIPLY_ASSIGN : TOY_TOKEN_MULTIPLY);
|
||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_DIVIDE_ASSIGN : TOY_TOKEN_DIVIDE);
|
||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MODULO_ASSIGN : TOY_TOKEN_MODULO);
|
||||
|
||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_NOT_EQUAL : TOY_TOKEN_NOT);
|
||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_EQUAL : TOY_TOKEN_ASSIGN);
|
||||
|
||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_LESS_EQUAL : TOY_TOKEN_LESS);
|
||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_GREATER_EQUAL : TOY_TOKEN_GREATER);
|
||||
|
||||
case '&': //TOKEN_AND not used
|
||||
if (advance(lexer) != '&') {
|
||||
return makeErrorToken(lexer, "Unexpected '&'");
|
||||
} else {
|
||||
return makeToken(lexer, TOY_TOKEN_AND);
|
||||
}
|
||||
|
||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
|
||||
|
||||
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
|
||||
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
|
||||
case ';': return makeToken(lexer, TOY_TOKEN_SEMICOLON);
|
||||
case ',': return makeToken(lexer, TOY_TOKEN_COMMA);
|
||||
case '.':
|
||||
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
return makeToken(lexer, TOY_TOKEN_REST);
|
||||
}
|
||||
return makeToken(lexer, TOY_TOKEN_DOT);
|
||||
|
||||
case '"':
|
||||
return makeString(lexer, c);
|
||||
//TODO: possibly support interpolated strings
|
||||
|
||||
default: {
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "Unexpected token: %c", c);
|
||||
return makeErrorToken(lexer, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void trim(char** s, int* l) { //all this to remove a newline?
|
||||
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
||||
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
||||
}
|
||||
|
||||
//for debugging
|
||||
void Toy_private_printToken(Toy_Token* token) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\t%d\t%d\t", token->type, token->line);
|
||||
|
||||
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
|
||||
printf("%.*s\t", token->length, token->lexeme);
|
||||
} else {
|
||||
char* keyword = Toy_findKeywordByType(token->type);
|
||||
|
||||
if (keyword != NULL) {
|
||||
printf("%s", keyword);
|
||||
} else {
|
||||
char* str = (char*)token->lexeme; //strip const-ness for trimming
|
||||
int length = token->length;
|
||||
trim(&str, &length);
|
||||
printf("%.*s", length, str);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
|
||||
lexer->commentsEnabled = enabled;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_token_types.h"
|
||||
|
||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||
typedef struct {
|
||||
const char* source;
|
||||
int start; //start of the token
|
||||
int current; //current position of the lexer
|
||||
int line; //track this for error handling
|
||||
bool commentsEnabled; //BUGFIX: enable comments (disabled in repl)
|
||||
} Toy_Lexer;
|
||||
|
||||
//tokens are intermediaries between lexers and parsers
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
const char* lexeme;
|
||||
int length;
|
||||
int line;
|
||||
} Toy_Token;
|
||||
|
||||
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
|
||||
TOY_API Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
|
||||
|
||||
//for debugging
|
||||
TOY_API void Toy_private_printToken(Toy_Token* token);
|
||||
|
||||
TOY_API void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
|
||||
@@ -0,0 +1,737 @@
|
||||
#include "toy_literal.h"
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//hash util functions
|
||||
static unsigned int hashString(const char* string, int length) {
|
||||
unsigned int hash = 2166136261u;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
hash *= string[i];
|
||||
hash ^= 16777619;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned int hashUInt(unsigned int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_freeLiteral(Toy_Literal literal) {
|
||||
//refstrings
|
||||
if (TOY_IS_STRING(literal)) {
|
||||
Toy_deleteRefString(TOY_AS_STRING(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(literal)) {
|
||||
Toy_deleteRefString(TOY_AS_IDENTIFIER(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//compounds
|
||||
if (TOY_IS_ARRAY(literal) || literal.type == TOY_LITERAL_ARRAY_INTERMEDIATE || literal.type == TOY_LITERAL_DICTIONARY_INTERMEDIATE || literal.type == TOY_LITERAL_TYPE_INTERMEDIATE) {
|
||||
Toy_freeLiteralArray(TOY_AS_ARRAY(literal));
|
||||
TOY_FREE(Toy_LiteralArray, TOY_AS_ARRAY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_DICTIONARY(literal)) {
|
||||
Toy_freeLiteralDictionary(TOY_AS_DICTIONARY(literal));
|
||||
TOY_FREE(Toy_LiteralDictionary, TOY_AS_DICTIONARY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//complex literals
|
||||
if (TOY_IS_FUNCTION(literal)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
|
||||
TOY_AS_FUNCTION(literal).scope = NULL;
|
||||
Toy_deleteRefFunction((Toy_RefFunction*)(TOY_AS_FUNCTION(literal).inner.ptr));
|
||||
}
|
||||
|
||||
if (TOY_IS_TYPE(literal) && TOY_AS_TYPE(literal).capacity > 0) {
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_Literal, TOY_AS_TYPE(literal).subtypes, TOY_AS_TYPE(literal).capacity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_private_isTruthy(Toy_Literal x) {
|
||||
if (TOY_IS_NULL(x)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Null is neither true nor false\n" TOY_CC_RESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_BOOLEAN(x)) {
|
||||
return TOY_AS_BOOLEAN(x);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
|
||||
return ((Toy_Literal){{ .identifier = { .ptr = ptr, .hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }},TOY_LITERAL_IDENTIFIER});
|
||||
}
|
||||
|
||||
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
|
||||
//grow the subtype array
|
||||
if (TOY_AS_TYPE(*lit).count + 1 > TOY_AS_TYPE(*lit).capacity) {
|
||||
int oldCapacity = TOY_AS_TYPE(*lit).capacity;
|
||||
|
||||
TOY_AS_TYPE(*lit).capacity = TOY_GROW_CAPACITY(oldCapacity);
|
||||
TOY_AS_TYPE(*lit).subtypes = TOY_GROW_ARRAY(Toy_Literal, TOY_AS_TYPE(*lit).subtypes, oldCapacity, TOY_AS_TYPE(*lit).capacity);
|
||||
}
|
||||
|
||||
//actually push
|
||||
((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count++ ] = subtype;
|
||||
return &((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count - 1 ];
|
||||
}
|
||||
|
||||
Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
||||
switch(original.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
case TOY_LITERAL_INTEGER:
|
||||
case TOY_LITERAL_FLOAT:
|
||||
//no copying needed
|
||||
return original;
|
||||
|
||||
case TOY_LITERAL_STRING: {
|
||||
return TOY_TO_STRING_LITERAL(Toy_copyRefString(TOY_AS_STRING(original)));
|
||||
}
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//preallocate enough space
|
||||
array->capacity = TOY_AS_ARRAY(original)->capacity;
|
||||
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, 0, array->capacity);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
|
||||
}
|
||||
|
||||
return TOY_TO_ARRAY_LITERAL(array);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//preallocate enough space
|
||||
dictionary->capacity = TOY_AS_DICTIONARY(original)->capacity;
|
||||
dictionary->entries = TOY_ALLOCATE(Toy_private_dictionary_entry, dictionary->capacity);
|
||||
|
||||
for (int i = 0; i < dictionary->capacity; i++) {
|
||||
dictionary->entries[i].key = TOY_TO_NULL_LITERAL;
|
||||
dictionary->entries[i].value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//copy each entry
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
|
||||
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
|
||||
Toy_setLiteralDictionary(dictionary, TOY_AS_DICTIONARY(original)->entries[i].key, TOY_AS_DICTIONARY(original)->entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION: {
|
||||
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(Toy_copyRefFunction( TOY_AS_FUNCTION(original).inner.ptr ));
|
||||
|
||||
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
//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: {
|
||||
Toy_Literal lit = TOY_TO_TYPE_LITERAL(TOY_AS_TYPE(original).typeOf, TOY_AS_TYPE(original).constant);
|
||||
|
||||
for (int i = 0; i < TOY_AS_TYPE(original).count; i++) {
|
||||
TOY_TYPE_PUSH_SUBTYPE(&lit, Toy_copyLiteral( ((Toy_Literal*)(TOY_AS_TYPE(original).subtypes))[i] ));
|
||||
}
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_OPAQUE: {
|
||||
return original; //literally a shallow copy
|
||||
}
|
||||
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_ARRAY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_TYPE_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
//no copying possible
|
||||
return original;
|
||||
|
||||
default:
|
||||
fprintf(stderr, TOY_CC_ERROR "Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
|
||||
//utility for other things
|
||||
if (lhs.type != rhs.type) {
|
||||
// ints and floats are compatible
|
||||
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
|
||||
if (TOY_IS_INTEGER(lhs)) {
|
||||
return TOY_AS_INTEGER(lhs) == TOY_AS_FLOAT(rhs);
|
||||
}
|
||||
else {
|
||||
return TOY_AS_FLOAT(lhs) == TOY_AS_INTEGER(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(lhs.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
return true; //can only be true because of the check above
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
return TOY_AS_BOOLEAN(lhs) == TOY_AS_BOOLEAN(rhs);
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
return TOY_AS_INTEGER(lhs) == TOY_AS_INTEGER(rhs);
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
return TOY_AS_FLOAT(lhs) == TOY_AS_FLOAT(rhs);
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
return Toy_equalsRefString(TOY_AS_STRING(lhs), TOY_AS_STRING(rhs));
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE:
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
||||
//mismatched sizes
|
||||
if (TOY_AS_ARRAY(lhs)->count != TOY_AS_ARRAY(rhs)->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//mismatched elements (in order)
|
||||
for (int i = 0; i < TOY_AS_ARRAY(lhs)->count; i++) {
|
||||
if (!Toy_literalsAreEqual( TOY_AS_ARRAY(lhs)->literals[i], TOY_AS_ARRAY(rhs)->literals[i] )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY:
|
||||
//relatively slow, especially when nested
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(lhs)->capacity; i++) {
|
||||
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
||||
//check it exists in rhs
|
||||
if (!Toy_existsLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//compare the values
|
||||
Toy_Literal val = Toy_getLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
|
||||
if (!Toy_literalsAreEqual(TOY_AS_DICTIONARY(lhs)->entries[i].value, val)) {
|
||||
Toy_freeLiteral(val);
|
||||
return false;
|
||||
}
|
||||
Toy_freeLiteral(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
return false; //functions are never equal
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
//check shortcuts
|
||||
if (TOY_HASH_I(lhs) != TOY_HASH_I(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Toy_equalsRefString(TOY_AS_IDENTIFIER(lhs), TOY_AS_IDENTIFIER(rhs));
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
//check types
|
||||
if (TOY_AS_TYPE(lhs).typeOf != TOY_AS_TYPE(rhs).typeOf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//const don't match
|
||||
if (TOY_AS_TYPE(lhs).constant != TOY_AS_TYPE(rhs).constant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check subtypes
|
||||
if (TOY_AS_TYPE(lhs).count != TOY_AS_TYPE(rhs).count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check array|dictionary signatures are the same (in order)
|
||||
if (TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_DICTIONARY) {
|
||||
for (int i = 0; i < TOY_AS_TYPE(lhs).count; i++) {
|
||||
if (!Toy_literalsAreEqual(((Toy_Literal*)(TOY_AS_TYPE(lhs).subtypes))[i], ((Toy_Literal*)(TOY_AS_TYPE(rhs).subtypes))[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
return false; //IDK what this is!
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't compare intermediate functions\n" TOY_CC_RESET);
|
||||
return false;
|
||||
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
return false;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in equality: %d\n" TOY_CC_RESET, lhs.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Toy_hashLiteral(Toy_Literal lit) {
|
||||
switch(lit.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
return 0;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
return TOY_AS_BOOLEAN(lit) ? 1 : 0;
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
|
||||
|
||||
case TOY_LITERAL_FLOAT: {
|
||||
unsigned int* ptr = (unsigned int*)(&TOY_AS_FLOAT(lit));
|
||||
return hashUInt(*ptr);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < TOY_AS_ARRAY(lit)->count; i++) {
|
||||
res += Toy_hashLiteral(TOY_AS_ARRAY(lit)->literals[i]);
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(lit)->capacity; i++) {
|
||||
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
||||
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].key);
|
||||
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].value);
|
||||
}
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
return -1; //can't hash these
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
return TOY_HASH_I(lit); //pre-computed
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
return -1; //not much i can really do
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
case TOY_LITERAL_ANY:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//utils
|
||||
static void stdoutWrapper(const char* output) {
|
||||
printf("%s", output);
|
||||
}
|
||||
|
||||
//buffer the prints
|
||||
static char* globalPrintBuffer = NULL;
|
||||
static size_t globalPrintCapacity = 0;
|
||||
static size_t globalPrintCount = 0;
|
||||
|
||||
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
||||
static char quotes = 0; //set to 0 to not show string quotes
|
||||
|
||||
static void printToBuffer(const char* str) {
|
||||
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
|
||||
int oldCapacity = globalPrintCapacity;
|
||||
|
||||
globalPrintCapacity = TOY_GROW_CAPACITY(globalPrintCapacity);
|
||||
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
||||
}
|
||||
|
||||
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str ? str : "\0");
|
||||
globalPrintCount += strlen(str);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_printLiteral(Toy_Literal literal) {
|
||||
Toy_printLiteralCustom(literal, stdoutWrapper);
|
||||
}
|
||||
|
||||
void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn printFn) {
|
||||
switch(literal.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
printFn("null");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
printFn(TOY_AS_BOOLEAN(literal) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_INTEGER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%d", TOY_AS_INTEGER(literal));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FLOAT: {
|
||||
char buffer[256];
|
||||
|
||||
if (TOY_AS_FLOAT(literal) - (int)TOY_AS_FLOAT(literal)) {
|
||||
snprintf(buffer, 256, "%g", TOY_AS_FLOAT(literal));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, 256, "%.1f", TOY_AS_FLOAT(literal));
|
||||
}
|
||||
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_STRING: {
|
||||
char buffer[TOY_MAX_STRING_LENGTH];
|
||||
if (!quotes) {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
|
||||
}
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
Toy_LiteralArray* ptr = TOY_AS_ARRAY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->count; i++) {
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->literals[i], printToBuffer);
|
||||
|
||||
if (i + 1 < ptr->count) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
}
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
Toy_LiteralDictionary* ptr = TOY_AS_DICTIONARY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
int delimCount = 0;
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->capacity; i++) {
|
||||
if (TOY_IS_NULL(ptr->entries[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delimCount++ > 0) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
||||
printToBuffer(":");
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
||||
}
|
||||
|
||||
//empty dicts MUST have a ":" printed
|
||||
if (ptr->count == 0) {
|
||||
printToBuffer(":");
|
||||
}
|
||||
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
printFn("(function)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%.*s", (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE: {
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the type correctly
|
||||
printToBuffer("<");
|
||||
|
||||
switch(TOY_AS_TYPE(literal).typeOf) {
|
||||
case TOY_LITERAL_NULL:
|
||||
printToBuffer("null");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
printToBuffer("bool");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
printToBuffer("int");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
printToBuffer("float");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
printToBuffer("string");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
//print all in the array
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY:
|
||||
printToBuffer("[");
|
||||
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i += 2) {
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
printToBuffer(":");
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
printToBuffer("function");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
printToBuffer("native");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
printToBuffer("identifier");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
printToBuffer("type");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
printToBuffer("opaque");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
printToBuffer("any");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print type: %d\n" TOY_CC_RESET, TOY_AS_TYPE(literal).typeOf);
|
||||
}
|
||||
|
||||
//const (printed last)
|
||||
if (TOY_AS_TYPE(literal).constant) {
|
||||
printToBuffer(" const");
|
||||
}
|
||||
|
||||
printToBuffer(">");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE:
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
|
||||
printFn("Unprintable literal found");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
printFn("(opaque)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
printFn("(any)");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print: %d\n" TOY_CC_RESET, literal.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_refstring.h"
|
||||
#include "toy_reffunction.h"
|
||||
|
||||
//forward delcare stuff
|
||||
struct Toy_Literal;
|
||||
struct Toy_Interpreter;
|
||||
struct Toy_LiteralArray;
|
||||
struct Toy_LiteralDictionary;
|
||||
struct Toy_Scope;
|
||||
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
|
||||
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
|
||||
typedef void (*Toy_PrintFn)(const char*);
|
||||
|
||||
typedef enum {
|
||||
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_IDENTIFIER,
|
||||
TOY_LITERAL_TYPE,
|
||||
TOY_LITERAL_OPAQUE,
|
||||
TOY_LITERAL_ANY,
|
||||
|
||||
//these are meta-level types - not for general use
|
||||
TOY_LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||
TOY_LITERAL_ARRAY_INTERMEDIATE, //used to process arrays in the compiler only
|
||||
TOY_LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
||||
TOY_LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||
TOY_LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
||||
TOY_LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
||||
TOY_LITERAL_FUNCTION_HOOK, //for handling hook functions within literals only
|
||||
TOY_LITERAL_INDEX_BLANK, //for blank indexing i.e. arr[:]
|
||||
} Toy_LiteralType;
|
||||
|
||||
typedef struct Toy_Literal {
|
||||
union {
|
||||
bool boolean; //1
|
||||
int integer; //4
|
||||
float number;//4
|
||||
|
||||
struct {
|
||||
Toy_RefString* ptr; //8
|
||||
//string hash?
|
||||
} string; //8
|
||||
|
||||
struct Toy_LiteralArray* array; //8
|
||||
struct Toy_LiteralDictionary* dictionary; //8
|
||||
|
||||
struct {
|
||||
union {
|
||||
Toy_RefFunction* ptr; //8
|
||||
Toy_NativeFn native; //8
|
||||
Toy_HookFn hook; //8
|
||||
} inner; //8
|
||||
struct Toy_Scope* scope; //8
|
||||
} function; //16
|
||||
|
||||
struct { //for variable names
|
||||
Toy_RefString* ptr; //8
|
||||
int hash; //4
|
||||
} identifier; //16
|
||||
|
||||
struct {
|
||||
struct Toy_Literal* subtypes; //8
|
||||
Toy_LiteralType typeOf; //4
|
||||
unsigned char capacity; //1
|
||||
unsigned char count; //1
|
||||
bool constant; //1
|
||||
} type; //16
|
||||
|
||||
struct {
|
||||
void* ptr; //8
|
||||
int tag; //4
|
||||
} opaque; //16
|
||||
|
||||
void* generic; //8
|
||||
} as; //16
|
||||
|
||||
Toy_LiteralType type; //4
|
||||
//4 - unused
|
||||
//shenanigans with byte alignment reduces the size of Toy_Literal
|
||||
} Toy_Literal;
|
||||
|
||||
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
|
||||
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
||||
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
||||
#define TOY_IS_FLOAT(value) ((value).type == TOY_LITERAL_FLOAT)
|
||||
#define TOY_IS_STRING(value) ((value).type == TOY_LITERAL_STRING)
|
||||
#define TOY_IS_ARRAY(value) ((value).type == TOY_LITERAL_ARRAY)
|
||||
#define TOY_IS_DICTIONARY(value) ((value).type == TOY_LITERAL_DICTIONARY)
|
||||
#define TOY_IS_FUNCTION(value) ((value).type == TOY_LITERAL_FUNCTION)
|
||||
#define TOY_IS_FUNCTION_NATIVE(value) ((value).type == TOY_LITERAL_FUNCTION_NATIVE)
|
||||
#define TOY_IS_FUNCTION_HOOK(value) ((value).type == TOY_LITERAL_FUNCTION_HOOK)
|
||||
#define TOY_IS_IDENTIFIER(value) ((value).type == TOY_LITERAL_IDENTIFIER)
|
||||
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
|
||||
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
|
||||
|
||||
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
|
||||
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
||||
#define TOY_AS_FLOAT(value) ((value).as.number)
|
||||
#define TOY_AS_STRING(value) ((value).as.string.ptr)
|
||||
#define TOY_AS_ARRAY(value) ((Toy_LiteralArray*)((value).as.array))
|
||||
#define TOY_AS_DICTIONARY(value) ((Toy_LiteralDictionary*)((value).as.dictionary))
|
||||
#define TOY_AS_FUNCTION(value) ((value).as.function)
|
||||
#define TOY_AS_FUNCTION_NATIVE(value) ((value).as.function.inner.native)
|
||||
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.inner.hook)
|
||||
#define TOY_AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||
#define TOY_AS_TYPE(value) ((value).as.type)
|
||||
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||
|
||||
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL})
|
||||
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN})
|
||||
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER})
|
||||
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT})
|
||||
#define TOY_TO_STRING_LITERAL(value) ((Toy_Literal){{ .string = { .ptr = value }},TOY_LITERAL_STRING})
|
||||
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY})
|
||||
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY})
|
||||
#define TOY_TO_FUNCTION_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .ptr = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION})
|
||||
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE})
|
||||
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK})
|
||||
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
||||
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE})
|
||||
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE})
|
||||
|
||||
//BUGFIX: For blank indexing
|
||||
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
||||
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
|
||||
|
||||
TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
||||
|
||||
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
|
||||
|
||||
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) (Toy_lengthRefFunction((lit).inner.ptr))
|
||||
|
||||
#define TOY_MAX_STRING_LENGTH 4096
|
||||
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
|
||||
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
|
||||
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||
|
||||
//BUGFIX: macros are not functions
|
||||
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
||||
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
|
||||
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
|
||||
|
||||
//utils
|
||||
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
|
||||
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
|
||||
TOY_API int Toy_hashLiteral(Toy_Literal lit);
|
||||
|
||||
//not thread-safe
|
||||
TOY_API void Toy_printLiteral(Toy_Literal literal);
|
||||
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn);
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "toy_literal_array.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLiteralArray(Toy_LiteralArray* array) {
|
||||
array->capacity = 0;
|
||||
array->count = 0;
|
||||
array->literals = NULL;
|
||||
}
|
||||
|
||||
void Toy_freeLiteralArray(Toy_LiteralArray* array) {
|
||||
//clean up memory
|
||||
for(int i = 0; i < array->count; i++) {
|
||||
Toy_freeLiteral(array->literals[i]);
|
||||
}
|
||||
|
||||
if (array->capacity > 0) {
|
||||
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
|
||||
Toy_initLiteralArray(array);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||
if (array->capacity < array->count + 1) {
|
||||
int oldCapacity = array->capacity;
|
||||
|
||||
array->capacity = TOY_GROW_CAPACITY(oldCapacity);
|
||||
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, oldCapacity, array->capacity);
|
||||
}
|
||||
|
||||
array->literals[array->count] = Toy_copyLiteral(literal);
|
||||
return array->count++;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
|
||||
if (array->count <= 0) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the return
|
||||
Toy_Literal ret = array->literals[array->count-1];
|
||||
|
||||
//null the existing data
|
||||
array->literals[array->count-1] = TOY_TO_NULL_LITERAL;
|
||||
|
||||
array->count--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//find a literal in the array that matches the "literal" argument
|
||||
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||
for (int i = 0; i < array->count; i++) {
|
||||
//not the same type
|
||||
if (array->literals[i].type != literal.type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//types match?
|
||||
if (Toy_literalsAreEqual(array->literals[i], literal)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value) {
|
||||
if (!TOY_IS_INTEGER(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = TOY_AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(array->literals[idx]);
|
||||
array->literals[idx] = Toy_copyLiteral(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index) {
|
||||
if (!TOY_IS_INTEGER(index)) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
int idx = TOY_AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
return Toy_copyLiteral(array->literals[idx]);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
|
||||
typedef struct Toy_LiteralArray {
|
||||
Toy_Literal* literals;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_LiteralArray;
|
||||
|
||||
TOY_API void Toy_initLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API void Toy_freeLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal);
|
||||
TOY_API Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value);
|
||||
TOY_API Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index);
|
||||
|
||||
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
|
||||
|
||||
//TODO: add a function to get the capacity & count
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//util functions
|
||||
static void setEntryValues(Toy_private_dictionary_entry* entry, Toy_Literal key, Toy_Literal value) {
|
||||
//much simpler now
|
||||
Toy_freeLiteral(entry->key);
|
||||
entry->key = Toy_copyLiteral(key);
|
||||
|
||||
Toy_freeLiteral(entry->value);
|
||||
entry->value = Toy_copyLiteral(value);
|
||||
}
|
||||
|
||||
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
|
||||
int index = hash % capacity;
|
||||
int start = index;
|
||||
|
||||
//increment once, so it can't equal start
|
||||
if (++index >= capacity) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
//literal probing and collision checking
|
||||
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
||||
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->value) && !mustExist) {
|
||||
//found a truly empty bucket
|
||||
return entry;
|
||||
}
|
||||
//else it's a tombstone - ignore
|
||||
} else {
|
||||
if (Toy_literalsAreEqual(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (++index >= capacity) {
|
||||
index = 0;
|
||||
}
|
||||
//index = (index + 1) % capacity;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void adjustEntryCapacity(Toy_private_dictionary_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
||||
//new entry space
|
||||
Toy_private_dictionary_entry* newEntries = TOY_ALLOCATE(Toy_private_dictionary_entry, capacity);
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
newEntries[i].key = TOY_TO_NULL_LITERAL;
|
||||
newEntries[i].value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//move the old array into the new one
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
if (TOY_IS_NULL((*dictionaryHandle)[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//place the key and value in the new array (reusing string memory)
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
|
||||
|
||||
entry->key = (*dictionaryHandle)[i].key;
|
||||
entry->value = (*dictionaryHandle)[i].value;
|
||||
}
|
||||
|
||||
//clear the old array
|
||||
if (oldCapacity > 0) {
|
||||
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
|
||||
}
|
||||
|
||||
*dictionaryHandle = newEntries;
|
||||
}
|
||||
|
||||
static bool setEntryArray(Toy_private_dictionary_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
|
||||
//expand array if needed
|
||||
if (contains + 1 > *capacityPtr * TOY_DICTIONARY_MAX_LOAD) {
|
||||
int oldCapacity = *capacityPtr;
|
||||
*capacityPtr = TOY_GROW_CAPACITY(*capacityPtr);
|
||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||
}
|
||||
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
||||
|
||||
//true = contains increase
|
||||
if (TOY_IS_NULL(entry->key)) {
|
||||
setEntryValues(entry, key, value);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
setEntryValues(entry, key, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void freeEntry(Toy_private_dictionary_entry* entry) {
|
||||
Toy_freeLiteral(entry->key);
|
||||
Toy_freeLiteral(entry->value);
|
||||
entry->key = TOY_TO_NULL_LITERAL;
|
||||
entry->value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
|
||||
if (array == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (!TOY_IS_NULL(array[i].key)) {
|
||||
freeEntry(&array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TOY_FREE_ARRAY(Toy_private_dictionary_entry, array, capacity);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
||||
dictionary->entries = NULL;
|
||||
dictionary->capacity = 0;
|
||||
dictionary->contains = 0;
|
||||
dictionary->count = 0;
|
||||
dictionary->capacity = 0;
|
||||
}
|
||||
|
||||
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||
if (dictionary->capacity > 0) {
|
||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||
dictionary->capacity = 0;
|
||||
dictionary->contains = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, Toy_hashLiteral(key));
|
||||
|
||||
if (increment) {
|
||||
dictionary->contains++;
|
||||
dictionary->count++;
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
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);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
return Toy_copyLiteral(entry->value);
|
||||
}
|
||||
else {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
freeEntry(entry);
|
||||
entry->value = TOY_TO_BOOLEAN_LITERAL(true); //tombstone
|
||||
dictionary->count--;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
//null & not tombstoned
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
|
||||
return entry != NULL && !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
|
||||
//TODO: benchmark this
|
||||
#define TOY_DICTIONARY_MAX_LOAD 0.75
|
||||
|
||||
typedef struct Toy_private_dictionary_entry {
|
||||
Toy_Literal key;
|
||||
Toy_Literal value;
|
||||
} Toy_private_dictionary_entry;
|
||||
|
||||
typedef struct Toy_LiteralDictionary {
|
||||
Toy_private_dictionary_entry* entries;
|
||||
int capacity;
|
||||
int count;
|
||||
int contains; //count + tombstones, for internal use
|
||||
} Toy_LiteralDictionary;
|
||||
|
||||
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||
|
||||
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
|
||||
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
|
||||
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "toy_memory.h"
|
||||
#include "toy_refstring.h"
|
||||
#include "toy_reffunction.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//default allocator
|
||||
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
//causes issues, so just skip out with a NO-OP (DISABLED for performance reasons)
|
||||
// if (newSize == 0 && oldSize == 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
if (newSize == 0) {
|
||||
free(pointer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* mem = realloc(pointer, newSize);
|
||||
|
||||
if (mem == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %zu, replacing %zu)\n" TOY_CC_RESET, newSize, oldSize);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
//static variables
|
||||
static Toy_MemoryAllocatorFn allocator = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
//exposed API
|
||||
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||
return allocator(pointer, oldSize, newSize);
|
||||
}
|
||||
|
||||
void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
|
||||
if (fn == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't be null)\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fn == Toy_reallocate) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't loop the Toy_reallocate function)\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
allocator = fn;
|
||||
Toy_setRefStringAllocatorFn(fn);
|
||||
Toy_setRefFunctionAllocatorFn(fn);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
||||
|
||||
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
|
||||
|
||||
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
|
||||
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||
|
||||
#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))
|
||||
|
||||
//implementation details
|
||||
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);
|
||||
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum Toy_Opcode {
|
||||
TOY_OP_EOF,
|
||||
|
||||
//do nothing
|
||||
TOY_OP_PASS,
|
||||
|
||||
//basic statements
|
||||
TOY_OP_ASSERT,
|
||||
TOY_OP_PRINT,
|
||||
|
||||
//data
|
||||
TOY_OP_LITERAL,
|
||||
TOY_OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
||||
TOY_OP_LITERAL_RAW, //forcibly get the raw value of the literal
|
||||
|
||||
//arithmetic operators
|
||||
TOY_OP_NEGATE,
|
||||
TOY_OP_ADDITION,
|
||||
TOY_OP_SUBTRACTION,
|
||||
TOY_OP_MULTIPLICATION,
|
||||
TOY_OP_DIVISION,
|
||||
TOY_OP_MODULO,
|
||||
TOY_OP_GROUPING_BEGIN,
|
||||
TOY_OP_GROUPING_END,
|
||||
|
||||
//variable stuff
|
||||
TOY_OP_SCOPE_BEGIN,
|
||||
TOY_OP_SCOPE_END,
|
||||
|
||||
TOY_OP_TYPE_DECL, //declare a type to be used (as a literal)
|
||||
TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
||||
|
||||
TOY_OP_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_FN_DECL, //declare a function to be used (as a literal)
|
||||
TOY_OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
|
||||
|
||||
TOY_OP_VAR_ASSIGN, //assign to a literal
|
||||
TOY_OP_VAR_ADDITION_ASSIGN,
|
||||
TOY_OP_VAR_SUBTRACTION_ASSIGN,
|
||||
TOY_OP_VAR_MULTIPLICATION_ASSIGN,
|
||||
TOY_OP_VAR_DIVISION_ASSIGN,
|
||||
TOY_OP_VAR_MODULO_ASSIGN,
|
||||
|
||||
TOY_OP_TYPE_CAST, //temporarily change a type of an atomic value
|
||||
TOY_OP_TYPE_OF, //get the type of a variable
|
||||
|
||||
TOY_OP_IMPORT,
|
||||
TOY_OP_EXPORT_removed,
|
||||
|
||||
//for indexing
|
||||
TOY_OP_INDEX,
|
||||
TOY_OP_INDEX_ASSIGN,
|
||||
TOY_OP_INDEX_ASSIGN_INTERMEDIATE,
|
||||
TOY_OP_DOT,
|
||||
|
||||
//comparison of values
|
||||
TOY_OP_COMPARE_EQUAL,
|
||||
TOY_OP_COMPARE_NOT_EQUAL,
|
||||
TOY_OP_COMPARE_LESS,
|
||||
TOY_OP_COMPARE_LESS_EQUAL,
|
||||
TOY_OP_COMPARE_GREATER,
|
||||
TOY_OP_COMPARE_GREATER_EQUAL,
|
||||
TOY_OP_INVERT, //for booleans
|
||||
|
||||
//logical operators
|
||||
TOY_OP_AND,
|
||||
TOY_OP_OR,
|
||||
|
||||
//jumps, and conditional jumps (absolute)
|
||||
TOY_OP_JUMP,
|
||||
TOY_OP_IF_FALSE_JUMP,
|
||||
TOY_OP_FN_CALL,
|
||||
TOY_OP_FN_RETURN,
|
||||
|
||||
//pop the stack at the end of a complex statement
|
||||
TOY_OP_POP_STACK,
|
||||
|
||||
//ternary shorthand
|
||||
TOY_OP_TERNARY,
|
||||
|
||||
//meta
|
||||
TOY_OP_FN_END, //different from SECTION_END
|
||||
TOY_OP_SECTION_END = 255,
|
||||
//TODO: add more
|
||||
|
||||
//prefix & postfix signals (used internally)
|
||||
TOY_OP_PREFIX,
|
||||
TOY_OP_POSTFIX,
|
||||
} Toy_Opcode;
|
||||
|
||||
+1945
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_ast_node.h"
|
||||
|
||||
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
|
||||
typedef struct {
|
||||
Toy_Lexer* lexer;
|
||||
bool error; //I've had an error
|
||||
bool panic; //I am processing an error
|
||||
|
||||
//track the last two outputs from the lexer
|
||||
Toy_Token current;
|
||||
Toy_Token previous;
|
||||
} Toy_Parser;
|
||||
|
||||
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
|
||||
TOY_API void Toy_freeParser(Toy_Parser* parser);
|
||||
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "toy_reffunction.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//memory allocation
|
||||
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||
static Toy_RefFunctionAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
Toy_RefFunction* refFunction = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * length);
|
||||
|
||||
if (refFunction == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//set the data
|
||||
refFunction->refCount = 1;
|
||||
refFunction->length = length;
|
||||
memcpy(refFunction->data, data, refFunction->length);
|
||||
|
||||
return refFunction;
|
||||
}
|
||||
|
||||
void Toy_deleteRefFunction(Toy_RefFunction* refFunction) {
|
||||
//decrement, then check
|
||||
refFunction->refCount--;
|
||||
if (refFunction->refCount <= 0) {
|
||||
allocate(refFunction, sizeof(size_t) + sizeof(int) + sizeof(char) * (refFunction->length + 1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_countRefFunction(Toy_RefFunction* refFunction) {
|
||||
return refFunction->refCount;
|
||||
}
|
||||
|
||||
size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction) {
|
||||
return refFunction->length;
|
||||
}
|
||||
|
||||
Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction) {
|
||||
//Cheaty McCheater Face
|
||||
refFunction->refCount++;
|
||||
return refFunction;
|
||||
}
|
||||
|
||||
Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction) {
|
||||
//create a new function, with a new refCount
|
||||
return Toy_createRefFunction(refFunction->data, refFunction->length);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
//memory allocation hook
|
||||
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
|
||||
|
||||
//the RefFunction structure
|
||||
typedef struct Toy_RefFunction {
|
||||
size_t length;
|
||||
int refCount;
|
||||
unsigned char data[];
|
||||
} Toy_RefFunction;
|
||||
|
||||
//API
|
||||
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
|
||||
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "toy_refstring.h"
|
||||
|
||||
//memory allocation
|
||||
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
Toy_RefString* Toy_createRefString(const char* cstring) {
|
||||
size_t length = strlen(cstring);
|
||||
|
||||
return Toy_createRefStringLength(cstring, length);
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
Toy_RefString* refString = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * (length + 1));
|
||||
|
||||
if (refString == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//set the data
|
||||
refString->refCount = 1;
|
||||
refString->length = length;
|
||||
strncpy(refString->data, cstring, refString->length);
|
||||
|
||||
refString->data[refString->length] = '\0'; //string terminator
|
||||
|
||||
return refString;
|
||||
}
|
||||
|
||||
void Toy_deleteRefString(Toy_RefString* refString) {
|
||||
//decrement, then check
|
||||
refString->refCount--;
|
||||
if (refString->refCount <= 0) {
|
||||
allocate(refString, sizeof(size_t) + sizeof(int) + sizeof(char) * (refString->length + 1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_countRefString(Toy_RefString* refString) {
|
||||
return refString->refCount;
|
||||
}
|
||||
|
||||
size_t Toy_lengthRefString(Toy_RefString* refString) {
|
||||
return refString->length;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_copyRefString(Toy_RefString* refString) {
|
||||
//Cheaty McCheater Face
|
||||
refString->refCount++;
|
||||
return refString;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString) {
|
||||
//create a new string, with a new refCount
|
||||
return Toy_createRefStringLength(refString->data, refString->length);
|
||||
}
|
||||
|
||||
const char* Toy_toCString(Toy_RefString* refString) {
|
||||
return refString->data;
|
||||
}
|
||||
|
||||
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs) {
|
||||
//same pointer
|
||||
if (lhs == rhs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//different length
|
||||
if (lhs->length != rhs->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
|
||||
}
|
||||
|
||||
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
|
||||
//get the rhs length
|
||||
size_t length = strlen(cstring);
|
||||
|
||||
//different length
|
||||
if (lhs->length != length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, cstring, lhs->length) == 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//memory allocation hook
|
||||
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
|
||||
|
||||
//the RefString structure
|
||||
typedef struct Toy_RefString {
|
||||
size_t length;
|
||||
int refCount;
|
||||
char data[];
|
||||
} Toy_RefString;
|
||||
|
||||
//API
|
||||
TOY_API Toy_RefString* Toy_createRefString(const char* cstring);
|
||||
TOY_API Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
|
||||
TOY_API void Toy_deleteRefString(Toy_RefString* refString);
|
||||
TOY_API int Toy_countRefString(Toy_RefString* refString);
|
||||
TOY_API size_t Toy_lengthRefString(Toy_RefString* refString);
|
||||
TOY_API Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
|
||||
TOY_API Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
|
||||
TOY_API const char* Toy_toCString(Toy_RefString* refString);
|
||||
TOY_API bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
|
||||
TOY_API bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
|
||||
|
||||
//TODO: merge refstring memory
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
#include "toy_scope.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
//run up the ancestor chain, freeing anything with 0 references left
|
||||
static void freeAncestorChain(Toy_Scope* scope) {
|
||||
while (scope != NULL) {
|
||||
Toy_Scope* next = scope->ancestor;
|
||||
|
||||
scope->references--;
|
||||
|
||||
if (scope->references <= 0) {
|
||||
Toy_freeLiteralDictionary(&scope->variables);
|
||||
Toy_freeLiteralDictionary(&scope->types);
|
||||
TOY_FREE(Toy_Scope, scope);
|
||||
}
|
||||
|
||||
scope = next;
|
||||
}
|
||||
}
|
||||
|
||||
//return false if invalid type
|
||||
static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal value, bool constCheck) {
|
||||
//for constants, fail if original != value
|
||||
if (constCheck && TOY_AS_TYPE(typeLiteral).constant && !Toy_literalsAreEqual(original, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//for any types
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ANY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//don't allow null types
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//always allow null values
|
||||
if (TOY_IS_NULL(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//for each type, if a mismatch is found, return false
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_BOOLEAN && !TOY_IS_BOOLEAN(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_INTEGER && !TOY_IS_INTEGER(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FLOAT && !TOY_IS_FLOAT(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_STRING && !TOY_IS_STRING(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY && !TOY_IS_ARRAY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_ARRAY(value)) {
|
||||
//check value's type
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new array variable that needs checking
|
||||
if (TOY_IS_NULL(original)) {
|
||||
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
|
||||
if (!checkType( ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check children
|
||||
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
|
||||
if (TOY_AS_ARRAY(original)->count <= i) {
|
||||
return true; //assume new entry pushed
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_AS_ARRAY(original)->literals[i], TOY_AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_DICTIONARY && !TOY_IS_DICTIONARY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_DICTIONARY(value)) {
|
||||
//check value's type
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_DICTIONARY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new dictionary variable that needs checking
|
||||
if (TOY_IS_NULL(original)) {
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
|
||||
//check the type of key and value
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check each child of value against the child of original
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
|
||||
if (TOY_IS_NULL(TOY_AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
|
||||
continue;
|
||||
}
|
||||
|
||||
//find the internal child of original that matches this child of value
|
||||
Toy_private_dictionary_entry* ptr = NULL;
|
||||
|
||||
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)) {
|
||||
ptr = &TOY_AS_DICTIONARY(original)->entries[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if not found, assume it's a new entry
|
||||
if (!ptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//check the type of key and value
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], ptr->key, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], ptr->value, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FUNCTION && !TOY_IS_FUNCTION(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_TYPE && !TOY_IS_TYPE(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_OPAQUE && !TOY_IS_OPAQUE(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
Toy_Scope* Toy_pushScope(Toy_Scope* ancestor) {
|
||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||
scope->ancestor = ancestor;
|
||||
Toy_initLiteralDictionary(&scope->variables);
|
||||
Toy_initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
||||
if (scope == NULL) { //CAN pop a null
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_Scope* ret = scope->ancestor;
|
||||
|
||||
//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++) {
|
||||
//handle keys, just in case
|
||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].key).scope);
|
||||
TOY_AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
|
||||
}
|
||||
|
||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].value)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].value).scope);
|
||||
TOY_AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
freeAncestorChain(scope);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
|
||||
if (original == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||
scope->ancestor = original->ancestor;
|
||||
Toy_initLiteralDictionary(&scope->variables);
|
||||
Toy_initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
//copy the contents of the dictionaries
|
||||
for (int i = 0; i < original->variables.capacity; i++) {
|
||||
if (!TOY_IS_NULL(original->variables.entries[i].key)) {
|
||||
Toy_setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < original->types.capacity; i++) {
|
||||
if (!TOY_IS_NULL(original->types.entries[i].key)) {
|
||||
Toy_setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
//returns false if error
|
||||
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type) {
|
||||
//don't redefine a variable within this scope
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TOY_IS_TYPE(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//store the type, for later checking on assignment
|
||||
Toy_setLiteralDictionary(&scope->types, key, type);
|
||||
|
||||
Toy_setLiteralDictionary(&scope->variables, key, TOY_TO_NULL_LITERAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
|
||||
while (scope != NULL) {
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
scope = scope->ancestor;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//return false if undefined, or can't be assigned
|
||||
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
|
||||
while (scope != NULL) {
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
scope = scope->ancestor;
|
||||
continue;
|
||||
}
|
||||
|
||||
//type checking
|
||||
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
|
||||
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
|
||||
|
||||
if (!checkType(typeLiteral, original, value, constCheck)) {
|
||||
Toy_freeLiteral(typeLiteral);
|
||||
Toy_freeLiteral(original);
|
||||
return false;
|
||||
}
|
||||
|
||||
//actually assign
|
||||
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
|
||||
|
||||
Toy_freeLiteral(typeLiteral);
|
||||
Toy_freeLiteral(original);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
|
||||
//optimized to reduce call stack
|
||||
while (scope != NULL) {
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
scope = scope->ancestor;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
|
||||
while (scope != NULL) {
|
||||
if (Toy_existsLiteralDictionary(&scope->types, key)) {
|
||||
return Toy_getLiteralDictionary(&scope->types, key);
|
||||
}
|
||||
|
||||
scope = scope->ancestor;
|
||||
}
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
typedef struct Toy_Scope {
|
||||
Toy_LiteralDictionary variables; //only allow identifiers as the keys
|
||||
Toy_LiteralDictionary types; //the types, indexed by identifiers
|
||||
struct Toy_Scope* ancestor;
|
||||
int references; //how many scopes point here
|
||||
} Toy_Scope;
|
||||
|
||||
TOY_API Toy_Scope* Toy_pushScope(Toy_Scope* scope);
|
||||
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||
TOY_API Toy_Scope* Toy_copyScope(Toy_Scope* original);
|
||||
|
||||
//returns false if error
|
||||
TOY_API bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
|
||||
TOY_API bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
|
||||
|
||||
//return false if undefined
|
||||
TOY_API bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
|
||||
TOY_API bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
|
||||
|
||||
TOY_API Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum Toy_TokenType {
|
||||
//types
|
||||
TOY_TOKEN_NULL,
|
||||
TOY_TOKEN_BOOLEAN,
|
||||
TOY_TOKEN_INTEGER,
|
||||
TOY_TOKEN_FLOAT,
|
||||
TOY_TOKEN_STRING,
|
||||
TOY_TOKEN_ARRAY,
|
||||
TOY_TOKEN_DICTIONARY,
|
||||
TOY_TOKEN_FUNCTION,
|
||||
TOY_TOKEN_OPAQUE,
|
||||
TOY_TOKEN_ANY,
|
||||
|
||||
//keywords and reserved words
|
||||
TOY_TOKEN_AS,
|
||||
TOY_TOKEN_ASSERT,
|
||||
TOY_TOKEN_BREAK,
|
||||
TOY_TOKEN_CLASS,
|
||||
TOY_TOKEN_CONST,
|
||||
TOY_TOKEN_CONTINUE,
|
||||
TOY_TOKEN_DO,
|
||||
TOY_TOKEN_ELSE,
|
||||
TOY_TOKEN_EXPORT,
|
||||
TOY_TOKEN_FOR,
|
||||
TOY_TOKEN_FOREACH,
|
||||
TOY_TOKEN_IF,
|
||||
TOY_TOKEN_IMPORT,
|
||||
TOY_TOKEN_IN,
|
||||
TOY_TOKEN_OF,
|
||||
TOY_TOKEN_PRINT,
|
||||
TOY_TOKEN_RETURN,
|
||||
TOY_TOKEN_TYPE,
|
||||
TOY_TOKEN_ASTYPE,
|
||||
TOY_TOKEN_TYPEOF,
|
||||
TOY_TOKEN_VAR,
|
||||
TOY_TOKEN_WHILE,
|
||||
|
||||
//literal values
|
||||
TOY_TOKEN_IDENTIFIER,
|
||||
TOY_TOKEN_LITERAL_TRUE,
|
||||
TOY_TOKEN_LITERAL_FALSE,
|
||||
TOY_TOKEN_LITERAL_INTEGER,
|
||||
TOY_TOKEN_LITERAL_FLOAT,
|
||||
TOY_TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
TOY_TOKEN_PLUS,
|
||||
TOY_TOKEN_MINUS,
|
||||
TOY_TOKEN_MULTIPLY,
|
||||
TOY_TOKEN_DIVIDE,
|
||||
TOY_TOKEN_MODULO,
|
||||
TOY_TOKEN_PLUS_ASSIGN,
|
||||
TOY_TOKEN_MINUS_ASSIGN,
|
||||
TOY_TOKEN_MULTIPLY_ASSIGN,
|
||||
TOY_TOKEN_DIVIDE_ASSIGN,
|
||||
TOY_TOKEN_MODULO_ASSIGN,
|
||||
TOY_TOKEN_PLUS_PLUS,
|
||||
TOY_TOKEN_MINUS_MINUS,
|
||||
TOY_TOKEN_ASSIGN,
|
||||
|
||||
//logical operators
|
||||
TOY_TOKEN_PAREN_LEFT,
|
||||
TOY_TOKEN_PAREN_RIGHT,
|
||||
TOY_TOKEN_BRACKET_LEFT,
|
||||
TOY_TOKEN_BRACKET_RIGHT,
|
||||
TOY_TOKEN_BRACE_LEFT,
|
||||
TOY_TOKEN_BRACE_RIGHT,
|
||||
TOY_TOKEN_NOT,
|
||||
TOY_TOKEN_NOT_EQUAL,
|
||||
TOY_TOKEN_EQUAL,
|
||||
TOY_TOKEN_LESS,
|
||||
TOY_TOKEN_GREATER,
|
||||
TOY_TOKEN_LESS_EQUAL,
|
||||
TOY_TOKEN_GREATER_EQUAL,
|
||||
TOY_TOKEN_AND,
|
||||
TOY_TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOY_TOKEN_QUESTION,
|
||||
TOY_TOKEN_COLON,
|
||||
TOY_TOKEN_SEMICOLON,
|
||||
TOY_TOKEN_COMMA,
|
||||
TOY_TOKEN_DOT,
|
||||
TOY_TOKEN_PIPE,
|
||||
TOY_TOKEN_REST,
|
||||
|
||||
//meta tokens
|
||||
TOY_TOKEN_PASS,
|
||||
TOY_TOKEN_ERROR,
|
||||
TOY_TOKEN_EOF,
|
||||
} Toy_TokenType;
|
||||
+2
-2
@@ -4,7 +4,7 @@ IDIR +=. ../source ../repl
|
||||
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||
LIBS +=
|
||||
ODIR = obj
|
||||
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c)
|
||||
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c) ../repl/repl_tools.c
|
||||
TESTS = $(wildcard test_*.c)
|
||||
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
|
||||
|
||||
@@ -14,7 +14,7 @@ all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
||||
|
||||
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
|
||||
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
|
||||
ifeq ($(shell uname),Linux)
|
||||
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
|
||||
valgrind --leak-check=full --track-origins=yes $@
|
||||
else
|
||||
$@
|
||||
|
||||
@@ -28,4 +28,22 @@ a %= 8;
|
||||
|
||||
assert a == 4, "%= failed";
|
||||
|
||||
//strings as special cases
|
||||
var s = "foo";
|
||||
|
||||
assert s + "bar" == "foobar", "string addition failed";
|
||||
assert s == "foo", "string addition failed (was too sticky)";
|
||||
|
||||
s += "bar";
|
||||
|
||||
assert s == "foobar", "string addition failed (wasn't sticky enough)";
|
||||
|
||||
//check order of operations
|
||||
assert 30 / 3 * 2 == 20, "Order of operations failed (raw numbers)";
|
||||
var x = 30;
|
||||
var y = 3;
|
||||
var z = 2;
|
||||
assert x / y * z == 20, "Order of operations failed (variables)";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,7 @@
|
||||
var s = "42";
|
||||
var t = "69";
|
||||
|
||||
assert int (s + t) - 1 == 4268, "casting parentheses failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -23,5 +23,8 @@ assert !false, "!false";
|
||||
var c = false;
|
||||
assert !c, "!c";
|
||||
|
||||
//test multiple comparisons
|
||||
assert 1 == 2 == false, "Left-accociative equality failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user