Compare commits

..

150 Commits

Author SHA1 Message Date
Kayne Ruse d3df01c1c4 Updated .gitignore 2023-02-23 03:33:52 +11:00
Ratstail91 cdca6fa45c Fixed directory in solution file 2023-02-22 20:06:48 +11:00
Kayne Ruse 1dde9d8f29 Improved error message in set() and push()
The actual issue was that the type check wasn't catching the issue, so
it reached the scope before it was caught. Fixed it, anyway.
2023-02-20 13:04:35 +00:00
Kayne Ruse 7f0f17b6e0 Patched up failures from Toy_parseIdentifierToValue
I really don't like that function - it needs to be replaced.
2023-02-20 06:11:30 +00:00
Kayne Ruse 3507104121 Fixed indexAccess potentially going awry with bad inputs
There's always one or two that slip through
2023-02-20 05:28:25 +00:00
Kayne Ruse 87de634e30 Updated version number to 1.0.0 2023-02-20 02:08:42 +00:00
Kayne Ruse 6fa224fa7b Hooks can't be dict keys, tweaked Toy_readFile 2023-02-18 16:47:38 +00:00
Kayne Ruse 8a68d864e6 Opaque type check added 2023-02-18 15:21:49 +00:00
Kayne Ruse 49f240ea07 Minor tweak 2023-02-18 12:15:23 +00:00
Kayne Ruse 3acbd7447a Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-18 11:57:22 +00:00
Kayne Ruse 6f126e6daa Minor tweaks and renames, as I'm documenting 2023-02-18 11:56:18 +00:00
Kayne Ruse 2adb9d9158 Tweaked lib runner API 2023-02-16 22:04:47 +00:00
Kayne Ruse 1668dca255 Tweaked some APIs, hid some functions I don't want in the API 2023-02-16 13:06:07 +00:00
Kayne Ruse 501ff6fff4 Chased a ghost for a few hours 2023-02-14 18:55:24 +00:00
Kayne Ruse 3845627fe5 Added release build to MSVC 2023-02-14 18:03:04 +00:00
Kayne Ruse cdae03bd54 String and identifier making fixed for MSVC, just in case 2023-02-14 17:38:10 +00:00
Kayne Ruse 7b501b71b5 commandLine now initializes with default values 2023-02-14 17:00:16 +00:00
Kayne Ruse 913738a4d1 Tweaked the runner test, should be orders of magnitude faster 2023-02-14 16:16:48 +00:00
Kayne Ruse 3312a38c7c Updated memusage tool 2023-02-14 16:05:43 +00:00
Kayne Ruse 71b57fd42c Fixed scripts for distribution 2023-02-14 10:35:08 +00:00
Kayne Ruse 453afbab41 Fixed a stupid bug in MSVC 2023-02-14 10:24:43 +00:00
Kayne Ruse 57af5a6d59 Tweaked some scripts 2023-02-14 09:21:22 +00:00
Kayne Ruse 0737b2a483 Dropped underscore functions in favour of UFCS 2023-02-14 08:37:31 +00:00
Kayne Ruse eae96d6403 Corrected the order of arguments to Toy_callLiteralFn() 2023-02-14 08:00:35 +00:00
Kayne Ruse b55b8e879e Added -n option to diable print newline 2023-02-13 15:51:38 +00:00
Kayne Ruse 1ed114b80d Allow for stmt to have empty clauses, resolved #58 2023-02-13 14:45:24 +00:00
Kayne Ruse eb8e522bf2 Merged standard and timer, resolved #48 2023-02-13 13:58:41 +00:00
Kayne Ruse 16b71ba6f4 Implemented quicksort in _sort() 2023-02-13 13:31:58 +00:00
Kayne Ruse 9725f3c6a3 Patched some very obscure bugs 2023-02-12 16:54:44 +00:00
Kayne Ruse 8653a2663f Added _indexOf 2023-02-12 14:32:26 +00:00
Kayne Ruse ab2cd5dc93 Removed lib timer properly, see #62 2023-02-12 14:19:14 +00:00
Kayne Ruse 724804a78a Playing with level.toy 2023-02-11 15:27:23 +00:00
Kayne Ruse 77a128e0f7 Added the -t option to the repl 2023-02-11 14:51:19 +00:00
Kayne Ruse 5343e1054d Straightened out file extensions 2023-02-11 14:26:55 +00:00
Kayne Ruse 3930ec0477 Tweaked README.md 2023-02-11 06:48:16 +00:00
Kayne Ruse 996744d7ec Resolved #59 2023-02-11 05:10:32 +00:00
Kayne Ruse c00b32017b Dummied out lib timer 2023-02-11 01:42:44 +00:00
Kayne Ruse 457014d577 Added MSVC build support, likely broke tests 2023-02-11 00:49:21 +00:00
Kayne Ruse be4cbf1ad6 Pack 'em up! 2023-02-10 21:53:38 +00:00
Kayne Ruse aeb008c684 Fixed unary negation bug, removed newline from print 2023-02-10 18:38:25 +00:00
Kayne Ruse 53012dbce1 Added _filter() 2023-02-10 15:41:38 +00:00
Kayne Ruse 4fe57f9562 Added _containsKey() and _containsValue() 2023-02-10 15:27:39 +00:00
Kayne Ruse 3ba2e420ea Added _every() and _some() 2023-02-10 15:00:15 +00:00
Kayne Ruse c81a139c97 Now handles unterminated block comments without freezing 2023-02-10 12:26:38 +00:00
Kayne Ruse 66ea684a90 Disabled comments in the repl 2023-02-10 12:11:42 +00:00
Kayne Ruse a26a6a56d0 Patched a pre/postfix increment/decrement segfault 2023-02-10 11:49:59 +00:00
Kayne Ruse ee226ea426 Strengthened constness for cstrings and bytecode 2023-02-10 08:52:38 +00:00
Kayne Ruse 76a0290290 Removed export keyword from README.md 2023-02-09 17:46:28 +00:00
Kayne Ruse 85dc24c4a6 Updated bug report template 2023-02-09 17:25:24 +00:00
Kayne Ruse 1804e380a0 tweak 2023-02-09 17:06:51 +00:00
Kayne Ruse 7567f4f3b2 Experimenting with issues 2023-02-09 17:05:20 +00:00
Kayne Ruse e8160eb9df Fixed string indexing with null 2023-02-09 16:46:36 +00:00
Kayne Ruse 276648630e trying to uncluster-bomb the builtin functions 2023-02-09 16:12:42 +00:00
Kayne Ruse e946a5f071 Patched segault in parser 2023-02-09 12:37:18 +00:00
Kayne Ruse 932401c26d Can now correctly parse escaped characters 2023-02-09 08:44:08 +00:00
Kayne Ruse fc67d6a18b Fixed a segfault 2023-02-09 08:24:16 +00:00
Kayne Ruse 60908c8bf3 Added panic state to Toy_Compiler to catch a certain condition 2023-02-09 07:58:55 +00:00
Kayne Ruse b8e3324acb tweak 2023-02-07 18:32:59 +00:00
Kayne Ruse 0fc8183799 Added _reduce 2023-02-06 09:46:20 +00:00
Kayne Ruse 9a6aa8d15e Added _trimBegin() and _trimEnd() 2023-02-06 06:11:17 +00:00
Kayne Ruse 0b9051cab2 Experimenting with numbers 2023-02-06 05:37:41 +00:00
Kayne Ruse 0d7e4db661 Fixed repl bug 2023-02-06 04:47:30 +00:00
Kayne Ruse d5c833b344 I wish I could test on windows 2023-02-06 01:05:44 +00:00
Kayne Ruse c875ae7a0e Added _forEach and _map, added tests for them 2023-02-06 00:51:07 +00:00
Kayne Ruse c0ec5ef28e Tested _concat() with clashing dict keys 2023-02-05 22:46:00 +00:00
Kayne Ruse be91de9535 Fixed a leak 2023-02-05 20:46:55 +00:00
Kayne Ruse 3088c4fe6d Implemented _concat 2023-02-05 20:45:31 +00:00
Kayne Ruse 9bd0cd23e7 Imported an example file for bytecode size testing, see #21 2023-02-05 15:40:17 +00:00
Kayne Ruse 29f8a698b4 Added a mustfail test 2023-02-05 15:19:04 +00:00
Kayne Ruse 41d274177a String concatenation restricted to + and += signs 2023-02-05 15:15:32 +00:00
Kayne Ruse 7ea249f723 Added the about library, resolved #51 2023-02-05 14:43:46 +00:00
Kayne Ruse 3949be3706 Quick potential bugfix 2023-02-05 13:34:39 +00:00
Kayne Ruse 0e932f24cc Added _toString() 2023-02-05 13:29:44 +00:00
Kayne Ruse 60225b733b Added _getKeys() and _getValues() 2023-02-05 12:49:12 +00:00
Kayne Ruse 15f99495a1 Aesthetics 2023-02-05 11:31:17 +00:00
Kayne Ruse 7b26527e95 Nesting index multiplication assignment fixed
This affects all arithmetic types applied to inner-nested compounds.
2023-02-05 06:28:24 +00:00
Kayne Ruse 386201b6e9 Added fib-memo.toy, fixed bugs until it worked 2023-02-04 17:01:58 +00:00
Kayne Ruse 8d278077b1 Added constant folding for strings, tweaked some error messages 2023-02-04 09:50:29 +00:00
Kayne Ruse 57c16d2ede Fixed an obscure memory leak, potential issue with lib_runner on linux 2023-02-04 15:30:46 +11:00
Kayne Ruse 2f1613e306 Caught an error in the compiler 2023-02-04 03:03:56 +00:00
Kayne Ruse 2776c87026 Ambiguous ternary syntax might be an issue 2023-02-04 02:59:10 +00:00
Kayne Ruse 8cc33f5fbb Added to ternary expression tests 2023-02-03 17:22:44 +00:00
Kayne Ruse 90f91a234a r is a bad character to escape 2023-01-31 12:38:42 +00:00
Kayne Ruse fcd1cdf70b Added a few characters that can be escaped 2023-01-31 12:37:18 +00:00
Kayne Ruse e265038547 Tested custom char trims 2023-01-29 08:38:26 +00:00
Kayne Ruse a357e0b0d4 Implemented _trim() 2023-01-29 08:30:31 +00:00
Kayne Ruse 2c5e3c6aa5 Started working on compound library 2023-01-29 07:23:52 +00:00
Kayne Ruse aeda0a0d94 Fixed pointer to int casting 2023-01-28 09:11:38 +00:00
Kayne Ruse ebbcba1b16 Enabled -std=c18 -pedantic, fixed all resulting errors
Thanks @gyrovorbis
2023-01-28 08:54:46 +00:00
Kayne Ruse 4bce10803e Added official support for mingw32 2023-01-28 07:56:11 +00:00
Kayne Ruse daee91904d Trying to test on windows 2023-01-28 06:20:36 +00:00
Kayne Ruse c397384766 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-01-28 06:11:54 +00:00
Kayne Ruse 667efc2feb Removed nonstandard C constructor attribute 2023-01-28 06:06:04 +00:00
Kayne Ruse f4469fc53d Removed file extension guard 2023-01-27 07:03:36 +00:00
Kayne Ruse c86c5800a7 Moved guard 2023-01-27 06:31:13 +00:00
Kayne Ruse e4d843ad3a Type check 2023-01-27 06:16:39 +00:00
Kayne Ruse 6667bce3d7 Finally fixed the buffer
I suppose valgrind, et. al. complaining is a good thing, overall.
2023-01-27 06:01:05 +00:00
Kayne Ruse 50b07da188 Put the clear BEFORE the return 2023-01-27 05:56:23 +00:00
Kayne Ruse 708db75760 Forgot to free this buffer 2023-01-27 05:53:35 +00:00
Kayne Ruse b632ce77ba Moved drive to filepath code into a function 2023-01-27 05:49:14 +00:00
Kayne Ruse b2c3dd894c Added a memusage tool 2023-01-25 15:11:22 +00:00
Kayne Ruse 9b21bfb53b Missed a rename 2023-01-25 13:54:28 +00:00
Kayne Ruse d3adac8a59 Fixed the parser - whoops 2023-01-25 13:17:57 +00:00
Kayne Ruse 5183037a99 Missed the globals 2023-01-25 13:15:16 +00:00
Kayne Ruse 2e2bee4fa3 Renemed all variables to fit into a namespace
Basically, all Toy varaibles, functions, etc. are prepended with "Toy_",
and macros are prepended with "TOY_". This is to reduce namespace
pollution, which was an issue pointed out to be - blame @GyroVorbis.

I've also bumped the minor version number - theoretically I should bump
the major number, but I'm not quite ready for 1.0 yet.
2023-01-25 12:55:55 +00:00
Kayne Ruse 047ccc5f16 Prepended file names with "toy_" 2023-01-23 21:45:52 +00:00
Kayne Ruse 59b0d15915 Fixed a pattern with parsing identifiers 2023-01-23 21:19:51 +00:00
Kayne Ruse 6d961cea78 Tweaked refstring, thanks SchuperJesse! 2023-01-23 08:21:51 +00:00
Kayne Ruse 33f360c9cf Fixed a way to have a bad type, thanks neuf! 2023-01-21 13:33:25 +00:00
Kayne Ruse d3516b4fc9 Adjusted the interpreter's version guard 2023-01-21 13:10:04 +00:00
Kayne Ruse df85d30553 Added loadScriptBytecode() 2023-01-21 04:27:01 +00:00
Kayne Ruse d57fca474f Fixed memory leak 2023-01-20 23:09:01 +00:00
Kayne Ruse 69f5151310 Finished runner library 2023-01-20 23:03:00 +00:00
Kayne Ruse c47ee68b3f Don't use the runner lib yet - will finish in the morning 2023-01-20 13:59:28 +00:00
Kayne Ruse 2c06c5e38b BUGFIX: Buffer overflow 2023-01-20 13:56:07 +00:00
Kayne Ruse e6e24ca19f Began working on runner library 2023-01-20 13:42:45 +00:00
Kayne Ruse 390f60e0ce F around and find out 2023-01-16 09:48:20 +00:00
Kayne Ruse 580e407b05 Squeaky clean! 2023-01-16 09:40:32 +00:00
Kayne Ruse 26bd476c2c Sanitized some addresses 2023-01-16 09:36:05 +00:00
Kayne Ruse b8b67fce3d Testing sanitiser 2023-01-16 09:15:20 +00:00
Kayne Ruse 68ed39fc45 Began working on a few bugs, thanks /u/skeeto! 2023-01-16 09:06:35 +00:00
Kayne Ruse aeecfabbbc Null pointer exceptions in parser unary function 2023-01-15 18:15:59 +00:00
Kayne Ruse 51740e2b9e Resolved #25, Indexing an array with a non-integer causes an error 2023-01-15 15:09:01 +00:00
Kayne Ruse 402abb647c Dummied out _index 2023-01-15 14:28:53 +00:00
Kayne Ruse 3aa7bf1877 Added patron list, which I forgot until now 2023-01-15 07:24:28 +00:00
Kayne Ruse ae1dc5841e Added ternary operator, resolved #46 2023-01-14 10:24:15 +00:00
Kayne Ruse 4b60d65203 Tweaked README.md 2023-01-13 16:31:56 +00:00
Kayne Ruse 3498baad9b Resolved #45, Exports region removed 2023-01-13 16:12:44 +00:00
Kayne Ruse 0649a141dd Moved time header includes, comment tweaks 2023-01-13 15:39:22 +00:00
Kayne Ruse 65e5905011 Updated license dates 2023-01-11 20:48:47 +00:00
Kayne Ruse bf64275aa9 Floats will always print with a trailing number 2023-01-08 13:26:04 +00:00
Kayne Ruse 08b400debf Added dictionary key mustfail test 2023-01-08 12:58:02 +00:00
Kayne Ruse 9ad2a6cf2e Tweaked build message 2023-01-08 23:46:04 +11:00
Kayne Ruse 8009f410a4 Added mustfail test, fixed compound type decl bug 2023-01-08 12:43:25 +00:00
Kayne Ruse 584fb115b6 Fixed the awful rule110 implementation 2022-11-28 16:48:45 +00:00
Kayne Ruse 70698a4a1a Simplified an optimisation 2022-11-27 02:25:33 +00:00
Kayne Ruse eb26d23363 Added FUNDING.yml, because why is this so popular? 2022-11-27 02:14:03 +00:00
Kayne Ruse c1625c53f4 Patched a bug in deepCopyRefString() 2022-11-26 21:33:46 +00:00
Kayne Ruse fb55f42d0e Tweaked where strlen() is called 2022-11-26 15:53:07 +00:00
Kayne Ruse e4ef35092f Merge pull request #43 from Ratstail91/dev
refstring implementation to handle strings and identifiers
2022-11-26 21:43:35 +11:00
Kayne Ruse cfafba589b All tests passing, repl builds successfully 2022-11-26 09:53:59 +00:00
Kayne Ruse f6367fa89d Updated interpreter 2022-11-26 09:43:19 +00:00
Kayne Ruse d199209772 Updated compiler 2022-11-26 06:58:38 +00:00
Kayne Ruse 4cf5c6a5bf Updated parser 2022-11-26 01:59:37 +00:00
Kayne Ruse 30c3a890ee Adjusted how AST Nodes are generated 2022-11-25 12:29:35 +00:00
Kayne Ruse 130ac980fe Enabled lexer tests 2022-11-23 13:02:43 +00:00
Kayne Ruse 923cf70c06 Minimal build of the refstrings in the literal structure 2022-11-23 12:52:49 +00:00
Kayne Ruse c7465e1204 Added API macro 2022-11-19 08:33:08 +00:00
Kayne Ruse 4518b59417 Added custom allocator support 2022-11-19 08:18:45 +00:00
Kayne Ruse 0b57f7efe7 Added release build targets, because bored 2022-11-16 22:05:47 +00:00
148 changed files with 16146 additions and 11440 deletions
+5
View File
@@ -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"]
+29
View File
@@ -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.
+17
View File
@@ -0,0 +1,17 @@
---
name: Feature Request
about: Suggest an idea
labels: enhancement
---
### Describe the feature youd like
A clear and concise description of what youd 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.
+19 -4
View File
@@ -2,18 +2,33 @@ name: Comprehensive Tests
on:
push:
branches: [ "main" ]
branches: [ "main", "dev" ]
pull_request:
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
View File
@@ -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
View File
@@ -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.
+10 -8
View File
@@ -14,18 +14,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 external libraries
* 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 +38,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 +61,13 @@ 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
+150
View File
@@ -0,0 +1,150 @@
<?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>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>
</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Users\kayne\Desktop\Toy\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</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_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_runner.h" />
<ClInclude Include="repl\lib_standard.h" />
<ClInclude Include="repl\repl_tools.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+159
View File
@@ -0,0 +1,159 @@
<?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_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_refstring.c" />
<ClCompile Include="source\toy_scope.c" />
</ItemGroup>
<ItemGroup>
<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_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_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
View File
@@ -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
-51
View File
@@ -1,51 +0,0 @@
DONE: rework type system
DONE: var decl with a type, but no value
DONE: type casting
DONE: remove optimization option
DONE: conditionals
DONE: if-then-else
DONE: chained if-then-else
DONE: optional block around a path if it's only one statement
DONE: while-then
DONE: for-then
DONE: break and continue statements
DONE: truthiness rethink
DONE: string concat with the + operator
DONE: increment & decrement operators
DONE: store compound types in variables
DONE: += -= *= /= %= operators
DONE: && and || operators
DONE: functions are invoked by calling their names
DONE: function arguments can have specified types
DONE: function returns can have specified types
DONE: closures are explicitly supported
DONE: functions are first-class citizens
DONE: functions take a set number of parameters
DONE: functions last argument can be a rest parameter
DONE: assert needs to kill the whole script, not just functions
DONE: native functions
DONE: global functions _get, _set, _push, _pop, _length, clear available
DONE: change comma to colon in dictionary definition
DONE: Address circular references
DONE: are compounds shallow or deep copies? Deep copies
DONE: third output stream, for lexer/parser/compiler/interpreter errors
DONE: Assertion-based test scripts
DONE: Import/export keywords
DONE: A way to check the type of a variable (typeOf keyword)
DONE: slice and dot notation around the builtin _index and _dot functions
DONE: maximum recursion/function depth
DONE: better sugar for _push, _pop, _length
DONE: nested compound assignment bug
DONE: hooks on the external libraries, triggered on import
TODO: standard library
TODO: external script runner library
TODO: document how it all works
TODO: packaging for release?
TODO: test embedding in a game
NOPE: a = b = c = 1;
NOPE: functions return a set number of values
NOPE: ternary operator?
NOPE: Nullish types?
+26 -2
View File
@@ -1,25 +1,49 @@
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
# export CFLAGS+=-fsanitize=address,undefined
export CFLAGS+=-std=c18 -pedantic -Werror
export TOY_OUTDIR = out
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
repl-static-release: clean $(TOY_OUTDIR) static-release
$(MAKE) -C repl release
#lib builds
library: $(TOY_OUTDIR)
$(MAKE) -j8 -C source library
static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static
library-release: $(TOY_OUTDIR)
$(MAKE) -j8 -C source library-release
static-release: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static-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)
+162
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+688
View File
@@ -0,0 +1,688 @@
#include "lib_runner.h"
#include "toy_memory.h"
#include "toy_interpreter.h"
#include "repl_tools.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.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_getFilePathLiteral(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_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 loadScriptBytecode\n");
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
//check for file extensions
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) {
interpreter->errorOutput("Bad binary file extension (expected .tb)\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
//check for break-out attempts
for (size_t i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
}
//load the bytecode
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);
TOY_FREE_ARRAY(char, filePath, realLength);
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;
}
//file system API
static Toy_LiteralDictionary Toy_driveDictionary;
void Toy_initDriveDictionary() {
Toy_initLiteralDictionary(&Toy_driveDictionary);
}
void Toy_freeDriveDictionary() {
Toy_freeLiteralDictionary(&Toy_driveDictionary);
}
Toy_LiteralDictionary* Toy_getDriveDictionary() {
return &Toy_driveDictionary;
}
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
size_t realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
//check for break-out attempts
for (size_t i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return result;
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "toy_common.h"
#include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
//file system API - these need to be set by the host
TOY_API void Toy_initDriveDictionary();
TOY_API void Toy_freeDriveDictionary();
TOY_API Toy_LiteralDictionary* Toy_getDriveDictionary();
#define TOY_OPAQUE_TAG_RUNNER 100
//file system API - for use with other libs
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+1679 -32
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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);
-411
View File
@@ -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, &microsecondLiteral)) {
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( copyString(buffer, strlen(buffer)), strlen(buffer));
}
else { //normal case
char buffer[128];
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(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(copyString(natives[i].name, strlen(natives[i].name)), strlen(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;
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "interpreter.h"
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
+3
View File
@@ -18,6 +18,9 @@ else
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -Wl,-rpath,. -L$(realpath $(shell pwd)/../$(TOY_OUTDIR)) $(LIBS)
endif
release: all
strip $(OUT)
$(OBJ): | $(ODIR)
$(ODIR):
+148 -57
View File
@@ -1,36 +1,44 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_standard.h"
#include "lib_timer.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>
#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, "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 +46,187 @@ 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_NODEERROR) {
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;
freeNode(node);
Toy_freeASTNode(node);
break;
}
writeCompiler(&compiler, node);
freeNode(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);
//lib setup (hacky - only really for this program)
Toy_initDriveDictionary();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//command line specific actions
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_freeDriveDictionary();
return 0;
}
//run from stdin
if (command.source) {
runSource(command.source);
if (Toy_commandLine.source) {
Toy_runSource(Toy_commandLine.source);
//lib cleanup
Toy_freeDriveDictionary();
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;
}
//run the binary file
Toy_runBinaryFile(Toy_commandLine.binaryfile);
//lib cleanup
Toy_freeDriveDictionary();
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_freeDriveDictionary();
return 0;
}
+62 -56
View File
@@ -1,44 +1,45 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_standard.h"
#include "lib_timer.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 +47,103 @@ 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);
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and leave
if (node->type == AST_NODEERROR) {
printf(ERROR "error node detected\n" RESET);
freeNode(node);
freeCompiler(&compiler);
freeParser(&parser);
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
return NULL;
}
writeCompiler(&compiler, node);
freeNode(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));
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, "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);
}
+7 -7
View File
@@ -2,13 +2,13 @@
#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);
+125
View File
@@ -0,0 +1,125 @@
import node;
//constants
var SPEED: int const = 10;
//variables
var parent: opaque = null;
var posX: int = 50;
var posY: int = 50;
var WIDTH: int const = 100;
var HEIGHT: int const = 100;
var xspeed: int = 0;
var yspeed: int = 0;
//accessors - variables are private, functions are public
fn getX(node: opaque) {
return posX;
}
fn getY(node: opaque) {
return posY;
}
//lifecycle functions
fn onInit(node: opaque) {
print "render.toy:onInit() called\n";
node.loadTexture("sprites:/character.png");
parent = node.getNodeParent();
}
fn onStep(node: opaque) {
posX += xspeed;
posY += yspeed;
}
fn onFree(node: opaque) {
print "render.toy:onFree() called\n";
node.freeTexture();
}
fn onDraw(node: opaque) {
// print "render.toy:onDraw() called\n";
var px = parent.callNode("getX");
var py = parent.callNode("getY");
if (px == null) {
px = 0;
}
if (py == null) {
py = 0;
}
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
}
//event functions
fn onKeyDown(node: opaque, event: string) {
if (event == "character_up") {
yspeed -= SPEED;
return;
}
if (event == "character_down") {
yspeed += SPEED;
return;
}
if (event == "character_left") {
xspeed -= SPEED;
return;
}
if (event == "character_right") {
xspeed += SPEED;
return;
}
}
fn onKeyUp(node: opaque, event: string) {
if (event == "character_up" && yspeed < 0) {
yspeed = 0;
return;
}
if (event == "character_down" && yspeed > 0) {
yspeed = 0;
return;
}
if (event == "character_left" && xspeed < 0) {
xspeed = 0;
return;
}
if (event == "character_right" && xspeed > 0) {
xspeed = 0;
return;
}
}
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
// print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")\n";
}
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")\n";
//jump to pos
posX = x - WIDTH / 2;
posY = y - HEIGHT / 2;
}
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")\n";
}
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
// print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")\n";
}
+21
View File
@@ -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;
}
+3 -5
View File
@@ -1,9 +1,7 @@
//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++) {
+90
View File
@@ -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]);
+40 -57
View File
@@ -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;
}
-13
View File
@@ -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();
-294
View File
@@ -1,294 +0,0 @@
#include "ast_node.h"
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>
void freeNodeCustom(ASTNode* node, bool freeSelf) {
//don't free a NULL node
if (node == NULL) {
return;
}
switch(node->type) {
case AST_NODEERROR:
//NO-OP
break;
case AST_NODELITERAL:
freeLiteral(node->atomic.literal);
break;
case AST_NODEUNARY:
freeNode(node->unary.child);
break;
case AST_NODEBINARY:
freeNode(node->binary.left);
freeNode(node->binary.right);
break;
case AST_NODEGROUPING:
freeNode(node->grouping.child);
break;
case AST_NODEBLOCK:
for (int i = 0; i < node->block.count; i++) {
freeNodeCustom(node->block.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
break;
case AST_NODECOMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeNodeCustom(node->compound.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
break;
case AST_NODEPAIR:
freeNode(node->pair.left);
freeNode(node->pair.right);
break;
case AST_NODEVAR_DECL:
freeLiteral(node->varDecl.identifier);
freeLiteral(node->varDecl.typeLiteral);
freeNode(node->varDecl.expression);
break;
case AST_NODEFN_DECL:
freeLiteral(node->fnDecl.identifier);
freeNode(node->fnDecl.arguments);
freeNode(node->fnDecl.returns);
freeNode(node->fnDecl.block);
break;
case AST_NODEFN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeNodeCustom(node->fnCollection.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case AST_NODEFN_CALL:
freeNode(node->fnCall.arguments);
break;
case AST_NODEPATH_IF:
case AST_NODEPATH_WHILE:
case AST_NODEPATH_FOR:
case AST_NODEPATH_BREAK:
case AST_NODEPATH_CONTINUE:
case AST_NODEPATH_RETURN:
freeNode(node->path.preClause);
freeNode(node->path.postClause);
freeNode(node->path.condition);
freeNode(node->path.thenPath);
freeNode(node->path.elsePath);
break;
case AST_NODEINCREMENT_PREFIX:
case AST_NODEINCREMENT_POSTFIX:
freeLiteral(node->increment.identifier);
break;
case AST_NODEIMPORT:
case AST_NODEEXPORT:
freeLiteral(node->import.identifier);
freeLiteral(node->import.alias);
break;
case AST_NODEINDEX:
case AST_NODEDOT:
freeNode(node->index.first);
freeNode(node->index.second);
freeNode(node->index.third);
break;
}
if (freeSelf) {
FREE(ASTNode, node);
}
}
void freeNode(ASTNode* node) {
freeNodeCustom(node, true);
}
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODELITERAL;
(*nodeHandle)->atomic.literal = copyLiteral(literal);
}
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODEUNARY;
(*nodeHandle)->unary.opcode = opcode;
(*nodeHandle)->unary.child = child;
}
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEBINARY;
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_NODEGROUPING;
tmp->grouping.child = *nodeHandle;
*nodeHandle = tmp;
}
void emitASTNodeBlock(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEBLOCK;
tmp->block.nodes = NULL;
tmp->block.capacity = 0;
tmp->block.count = 0;
*nodeHandle = tmp;
}
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODECOMPOUND;
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) {
//assume the node has already been allocated
node->type = AST_NODEPAIR;
node->pair.left = left;
node->pair.right = right;
}
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEVAR_DECL;
tmp->varDecl.identifier = identifier;
tmp->varDecl.typeLiteral = typeLiteral;
tmp->varDecl.expression = expression;
*nodeHandle = tmp;
}
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_DECL;
tmp->fnDecl.identifier = identifier;
tmp->fnDecl.arguments = arguments;
tmp->fnDecl.returns = returns;
tmp->fnDecl.block = block;
*nodeHandle = tmp;
}
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_CALL;
tmp->fnCall.arguments = arguments;
tmp->fnCall.argumentCount = argumentCount;
*nodeHandle = tmp;
}
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_COLLECTION;
tmp->fnCollection.nodes = NULL;
tmp->fnCollection.capacity = 0;
tmp->fnCollection.count = 0;
*nodeHandle = tmp;
}
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = type;
tmp->path.preClause = preClause;
tmp->path.postClause = postClause;
tmp->path.condition = condition;
tmp->path.thenPath = thenPath;
tmp->path.elsePath = elsePath;
*nodeHandle = tmp;
}
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINCREMENT_PREFIX;
tmp->increment.identifier = copyLiteral(identifier);
tmp->increment.increment = increment;
*nodeHandle = tmp;
}
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINCREMENT_POSTFIX;
tmp->increment.identifier = copyLiteral(identifier);
tmp->increment.increment = increment;
*nodeHandle = tmp;
}
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = mode;
tmp->import.identifier = copyLiteral(identifier);
tmp->import.alias = copyLiteral(alias);
*nodeHandle = tmp;
}
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINDEX;
tmp->index.first = first;
tmp->index.second = second;
tmp->index.third = third;
*nodeHandle = tmp;
}
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEDOT;
tmp->index.first = first;
tmp->index.second = NULL;
tmp->index.third = NULL;
*nodeHandle = tmp;
}
-175
View File
@@ -1,175 +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_NODEERROR,
AST_NODELITERAL, //a simple value
AST_NODEUNARY, //one child + opcode
AST_NODEBINARY, //two children, left and right + opcode
AST_NODEGROUPING, //one child
AST_NODEBLOCK, //contains a sub-node array
AST_NODECOMPOUND, //contains a sub-node array
AST_NODEPAIR, //contains a left and right
AST_NODEVAR_DECL, //contains identifier literal, typenode, expression definition
AST_NODEFN_DECL, //containd identifier literal, arguments node, returns node, block node
AST_NODEFN_COLLECTION, //parts of a function
AST_NODEFN_CALL,
AST_NODEPATH_IF, //for control flow
AST_NODEPATH_WHILE, //for control flow
AST_NODEPATH_FOR, //for control flow
AST_NODEPATH_BREAK, //for control flow
AST_NODEPATH_CONTINUE, //for control flow
AST_NODEPATH_RETURN,
AST_NODEINCREMENT_PREFIX,
AST_NODEINCREMENT_POSTFIX,
AST_NODEIMPORT,
AST_NODEEXPORT,
AST_NODEINDEX,
AST_NODEDOT,
} ASTNodeType;
typedef struct NodeLiteral {
ASTNodeType type;
Literal literal;
} NodeLiteral;
typedef struct NodeUnary {
ASTNodeType type;
Opcode opcode;
ASTNode* child;
} NodeUnary;
typedef struct NodeBinary {
ASTNodeType type;
Opcode opcode;
ASTNode* left;
ASTNode* right;
} NodeBinary;
typedef struct NodeGrouping {
ASTNodeType type;
ASTNode* child;
} NodeGrouping;
typedef struct NodeBlock {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeBlock;
typedef struct NodeCompound {
ASTNodeType type;
LiteralType literalType;
ASTNode* nodes;
int capacity;
int count;
} NodeCompound;
typedef struct NodePair {
ASTNodeType type;
ASTNode* left;
ASTNode* right;
} NodePair;
typedef struct NodeVarDecl {
ASTNodeType type;
Literal identifier;
Literal typeLiteral;
ASTNode* expression;
} NodeVarDecl;
typedef struct NodeFnDecl {
ASTNodeType type;
Literal identifier;
ASTNode* arguments;
ASTNode* returns;
ASTNode* block;
} NodeFnDecl;
typedef struct NodeFnCollection {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeFnCollection;
typedef struct NodeFnCall {
ASTNodeType type;
ASTNode* arguments;
int argumentCount;
} NodeFnCall;
typedef struct NodePath {
ASTNodeType type;
ASTNode* preClause;
ASTNode* postClause;
ASTNode* condition;
ASTNode* thenPath;
ASTNode* elsePath;
} NodePath;
typedef struct NodeIncrement {
ASTNodeType type;
Literal identifier;
int increment;
} NodeIncrement;
typedef struct NodeImport {
ASTNodeType type;
Literal identifier;
Literal alias;
} NodeImport;
typedef struct NodeIndex {
ASTNodeType type;
ASTNode* first;
ASTNode* second;
ASTNode* third;
} NodeIndex;
union _node {
ASTNodeType type;
NodeLiteral atomic;
NodeUnary unary;
NodeBinary binary;
NodeGrouping grouping;
NodeBlock block;
NodeCompound compound;
NodePair pair;
NodeVarDecl varDecl;
NodeFnDecl fnDecl;
NodeFnCollection fnCollection;
NodeFnCall fnCall;
NodePath path;
NodeIncrement increment;
NodeImport import;
NodeIndex index;
};
TOY_API void freeNode(ASTNode* node);
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
void emitASTNodeGrouping(ASTNode** nodeHandle);
void emitASTNodeBlock(ASTNode** nodeHandle);
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right);
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount);
void emitASTNodeFnCollection(ASTNode** nodeHandle);
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias);
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first);
-1345
View File
File diff suppressed because it is too large Load Diff
-11
View File
@@ -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);
-1164
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -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);
-30
View File
@@ -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"
-2543
View File
File diff suppressed because it is too large Load Diff
-57
View File
@@ -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
-77
View File
@@ -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;
}
-14
View File
@@ -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
View File
@@ -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");
}
-26
View File
@@ -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);
-703
View File
@@ -1,703 +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 hash(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) {
if (IS_STRING(literal)) {
FREE_ARRAY(char, AS_STRING(literal), literal.as.string.length + 1);
return;
}
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;
}
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_IDENTIFIER(literal)) {
FREE_ARRAY(char, AS_IDENTIFIER(literal), literal.as.identifier.length + 1);
return;
}
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(char* str, int length) {
return ((Literal){LITERAL_STRING, { .string.ptr = (char*)str, .string.length = length }});
}
Literal _toIdentifierLiteral(char* str, int length) {
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = (char*)str, .identifier.length = length, .identifier.hash = hashString(str, length) }});
}
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(copyString(AS_STRING(original), original.as.string.length), original.as.string.length);
}
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(copyString(AS_IDENTIFIER(original), original.as.identifier.length), original.as.identifier.length);
}
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;
}
}
char* copyString(char* original, int length) {
//make a local copy of the char array
char* buffer = ALLOCATE(char, length + 1);
strncpy(buffer, original, length);
buffer[length] = '\0';
return buffer;
}
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:
if (lhs.as.string.length != rhs.as.string.length) {
return false;
}
return !strncmp(AS_STRING(lhs), AS_STRING(rhs), lhs.as.string.length);
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) && lhs.as.identifier.length != rhs.as.identifier.length) {
return false;
}
return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), lhs.as.identifier.length);
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 hash((unsigned int)AS_INTEGER(lit));
case LITERAL_FLOAT:
return hash(*(unsigned int*)(&AS_FLOAT(lit)));
case LITERAL_STRING:
return hashString(AS_STRING(lit), strlen(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 hash(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 hash(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", (int)strlen(AS_STRING(literal)), AS_STRING(literal));
}
else {
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)strlen(AS_STRING(literal)), 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", (int)strlen( AS_IDENTIFIER(literal) ), 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);
}
}
-128
View File
@@ -1,128 +0,0 @@
#pragma once
#include "toy_common.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 {
char* ptr;
int length;
} string;
void* array;
void* dictionary;
struct {
void* bytecode;
void* scope;
int length;
} function;
struct { //for variable names
char* ptr;
int length;
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;
} 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, l) _toStringLiteral(value, l)
#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, l) _toIdentifierLiteral(value, l)
#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(char* str, int length);
TOY_API Literal _toIdentifierLiteral(char* str, int length);
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
//utils
TOY_API Literal copyLiteral(Literal original);
TOY_API char* copyString(char* original, int length);
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*));
-100
View File
@@ -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]);
}
-20
View File
@@ -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);
-219
View File
@@ -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));
}
-29
View File
@@ -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);
+6
View File
@@ -34,6 +34,12 @@ library: $(OBJ)
static: $(OBJ)
ar crs ../$(TOY_OUTDIR)/lib$(OUTNAME).a $(OBJ)
library-release: $(OBJ) library
strip $(OUT)
static-release: $(OBJ) static
strip -d ../$(TOY_OUTDIR)/lib$(OUTNAME).a
$(OBJ): | $(ODIR)
$(ODIR):
-29
View File
@@ -1,29 +0,0 @@
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
#include <stdlib.h>
void* reallocate(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" ERROR, (int)newSize, (long int)pointer, (int)oldSize);
exit(-1);
}
return mem;
}
-14
View File
@@ -1,14 +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)
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
-84
View File
@@ -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;
-1783
View File
File diff suppressed because it is too large Load Diff
-20
View File
@@ -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);
-303
View File
@@ -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);
}
-25
View File
@@ -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);
-92
View File
@@ -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;
+397
View File
@@ -0,0 +1,397 @@
#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:
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:
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:
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;
}
+273
View File
@@ -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);
+1593
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -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);
+60 -49
View File
@@ -15,77 +15,89 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
STATIC_ASSERT(sizeof(unsigned short) == 2);
STATIC_ASSERT(sizeof(unsigned int) == 4);
#ifndef TOY_EXPORT
//declare the singleton
Command command;
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,
.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], "-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 +107,30 @@ 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(" -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
+25 -26
View File
@@ -4,30 +4,30 @@
#include <stddef.h>
#include <stdint.h>
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 6
#define TOY_VERSION_PATCH 2
#define TOY_VERSION_MAJOR 1
#define TOY_VERSION_MINOR 0
#define TOY_VERSION_PATCH 1
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
//platform exports/imports
#if defined(__linux__)
#define TOY_API extern
#include <time.h>
#include <sys/time.h>
//platform/compiler-specific instructions
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
#elif defined(_WIN32) || defined(WIN32)
#define TOY_API
#include <time.h>
#include <sys/time.h>
#define TOY_API extern
#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
typedef struct {
bool error;
@@ -38,17 +38,16 @@ typedef struct {
char* compilefile;
char* outfile; //defaults to out.tb
char* source;
char* initialfile;
bool enablePrintNewline;
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
+22
View File
@@ -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);
+64
View File
@@ -0,0 +1,64 @@
#pragma once
//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
File diff suppressed because it is too large Load Diff
+51
View File
@@ -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
+77
View File
@@ -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;
}
+14
View File
@@ -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);
+383
View File
@@ -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;
}
+29
View File
@@ -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);
+728
View File
@@ -0,0 +1,728 @@
#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_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(literal));
}
if (TOY_IS_TYPE(literal)) {
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_toStringLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){{ .string = { .ptr = ptr }},TOY_LITERAL_STRING, 0});
}
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, 0});
}
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);
//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);
//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: {
unsigned char* buffer = TOY_ALLOCATE(unsigned char, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
memcpy(buffer, TOY_AS_FUNCTION(original).inner.bytecode, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(buffer, TOY_AS_FUNCTION_BYTECODE_LENGTH(original));
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
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: {
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: {
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: {
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:
return hashUInt(*(unsigned int*)(&TOY_AS_FLOAT(lit)));
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);
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);
}
}
+155
View File
@@ -0,0 +1,155 @@
#pragma once
#include "toy_common.h"
#include "toy_refstring.h"
//forward delcare stuff
struct Toy_Literal;
struct Toy_Interpreter;
struct Toy_LiteralArray;
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
void* array; //8
void* dictionary; //8
struct {
union {
void* bytecode; //8
Toy_NativeFn native; //8
Toy_HookFn hook; //8
} inner; //8
void* scope; //8
} function; //16
struct { //for variable names
Toy_RefString* ptr; //8
int hash; //4
} identifier; //16
struct {
void* 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
} as; //16
Toy_LiteralType type; //4
int bytecodeLength; //4 - 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, 0})
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN, 0})
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER, 0})
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT, 0})
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY, 0})
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY, 0})
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){{ .function = { .inner = { .bytecode = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION, l})
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE, 0})
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK, 0})
#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, 0})
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE, 0})
//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, 0})
TOY_API void Toy_freeLiteral(Toy_Literal literal);
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) ((lit).bytecodeLength)
#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_toStringLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
//utils
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);
+98
View File
@@ -0,0 +1,98 @@
#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]);
}
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]);
}
+22
View File
@@ -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
+219
View File
@@ -0,0 +1,219 @@
#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) {
//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
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;
}
}
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
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 = TOY_GROW_CAPACITY(0);
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
}
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
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 !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
}
+29
View File
@@ -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);
+52
View File
@@ -0,0 +1,52 @@
#include "toy_memory.h"
#include "toy_refstring.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) {
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, 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);
}
+21
View File
@@ -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);
+90
View File
@@ -0,0 +1,90 @@
#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
} Toy_Opcode;
+1902
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -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);
+93
View File
@@ -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;
}
+31
View File
@@ -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
+328
View File
@@ -0,0 +1,328 @@
#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) {
scope->references--;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
}
if (scope->references > 0) {
return;
}
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
//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) {
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) {
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
}
//return false if undefined, or can't be assigned
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
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;
}
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
}
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
//dead end
if (scope == NULL) {
return TOY_TO_NULL_LITERAL;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getScopeType(scope->ancestor, key);
}
return Toy_getLiteralDictionary(&scope->types, key);
}
+26
View File
@@ -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);
+93
View File
@@ -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;
+3 -4
View File
@@ -3,10 +3,9 @@ CC=gcc
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)
TESTS = $(wildcard *.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))
.PRECIOUS: $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
@@ -15,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
$@
+11
View File
@@ -28,4 +28,15 @@ 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)";
print "All good";
+12
View File
@@ -0,0 +1,12 @@
fn fib(n : int) {
if (n < 2) {
return n;
}
return fib(n-1) + fib(n-2);
}
for (var i = 0; i < 20; i++) {
var res = fib(i);
print string i + ": " + string res;
}
+17 -17
View File
@@ -2,11 +2,11 @@
var a = [1, 2, 3];
var b = [4, 5, 6];
assert _length(a) == _length(b), "a and b lengths are wrong";
assert length(a) == length(b), "a and b lengths are wrong";
var acc = 0;
for (var i = 0; i < _length(a); i++) {
acc += _get(a, i) * _get(b, i);
for (var i = 0; i < length(a); i++) {
acc += get(a, i) * get(b, i);
}
assert acc == 32, "dot product failed";
@@ -15,38 +15,38 @@ assert acc == 32, "dot product failed";
//assume the args are matrices
fn matrix(first, second) {
//get the matrix size
var l1 = _length(first); //rows
var l2 = _length(_get(first, 0)); //cols
var l1 = length(first); //rows
var l2 = length(get(first, 0)); //cols
var l3 = _length(second); //rows
var l4 = _length(_get(second, 0)); //cols
var l3 = length(second); //rows
var l4 = length(get(second, 0)); //cols
//pre-allocate the matrix
var row = [];
for (var j = 0; j < l4; j++) {
_push(row, 0);
push(row, 0);
}
var result = [];
for (var i = 0; i < l1; i++) {
_push(result, row);
push(result, row);
}
//assign the values
for (var i = 0; i < _length(first); i++) {
for (var i = 0; i < length(first); i++) {
//select each element of "first"
var firstElement = _get(first, i);
var firstElement = get(first, i);
//for each element of second
for (var i2 = 0; i2 < _length(second); i2++) {
for (var j2 = 0; j2 < _length(_get(second, 0)); j2++) {
for (var i2 = 0; i2 < length(second); i2++) {
for (var j2 = 0; j2 < length(get(second, 0)); j2++) {
var val = _get(_get(first, i), i2) * _get(_get(second, i2), j2);
var val = get(get(first, i), i2) * get(get(second, i2), j2);
//TODO: needs better notation than this tmpRow variable
var tmpRow = _get(result, i);
_set(tmpRow, j2, val);
_set(result, i, tmpRow);
var tmpRow = get(result, i);
set(tmpRow, j2, val);
set(result, i, tmpRow);
//result[ i ][ j2 ] += first[i][i2] * second[i2][j2]
}
+1 -1
View File
@@ -9,7 +9,7 @@ It appears to be a compiler issue, see issue #38 for more info.
*/
fn _getValue(self) {
fn getValue(self) {
return self;
}
+3 -3
View File
@@ -1,10 +1,10 @@
//test function chaining with the dot operator
fn _identity(self) {
fn identity(self) {
return self;
}
fn _check(self) {
fn check(self) {
assert self == 42, "dot chaining failed";
return self;
}
@@ -20,7 +20,7 @@ val
//test the value is actually altered
fn _increment(self) {
fn increment(self) {
return self + 1;
}
+1 -1
View File
@@ -1,5 +1,5 @@
fn _add(self, inc) {
fn add(self, inc) {
return self + inc;
}
+1 -1
View File
@@ -63,7 +63,7 @@ extra("one", "two", "three", "four", "five", "six", "seven");
//test underscore functions
fn _example(self, a, b, c) {
fn example(self, a, b, c) {
assert a == "a", "underscore failed (a)";
assert b == "b", "underscore failed (b)";
assert c == "c", "underscore failed (c)";
-52
View File
@@ -1,52 +0,0 @@
//test basic import/export
{
var variable: int = 42;
export variable as field;
}
{
import field as value;
assert value == 42, "import/export failed";
}
//test functions using import/export
{
fn f() {
import field;
assert field == 42, "import in function failed";
}
}
//test importing/exporting of functions
{
fn func() {
return 69;
}
export func;
}
{
import func;
assert func() == 69, "import/export of functions failed";
}
//test that variables retain their types with the typeof keyword
{
var t: type = int;
export t;
}
{
import t;
assert typeof t == type, "type retention failed";
}
print "All good";
+10
View File
@@ -72,6 +72,16 @@
}
//test nested indexing multipliciation assignment
{
var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
a[1][1] *= 10;
assert a == [[1, 2, 3], [4, 50, 6], [7, 8, 9]], "nested indexing multipliciation assignment failed";
}
//test combine example
{
fn combine(a, b, c) {
@@ -0,0 +1,15 @@
/*
This ensures that when indexing on both sides of an assignment,
it works correctly.
*/
var a = [1, 2, 3];
var b = [4, 5, 6];
a[1] = b[1];
assert a == [1, 5, 3], "index assignment both failed";
print "All good";

Some files were not shown because too many files have changed in this diff Show More