Compare commits
455 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f6ec6a8c73 | |||
| 2157b2f540 | |||
| 1481216e69 | |||
| f25f389b4e | |||
| deff784df8 | |||
| 54e82846c3 | |||
| 67fce427eb | |||
| 8a2cb61435 | |||
| 50d03e28fc | |||
| 763581c73b | |||
| cdb2613e5d | |||
| 733df87c08 | |||
| bfd506f497 | |||
| 18b59c9e84 | |||
| d3eb31d964 | |||
| 07f4a98b95 | |||
| 0949fd6ff9 | |||
| 03e5096f10 | |||
| bb81b8c474 | |||
| cf6db57787 | |||
| 17f0e4476b | |||
| 1095e1a885 | |||
| 2edfbbe3ef | |||
| 4b83f1f0d6 | |||
| e2fa1cf2e8 | |||
| a04d2c4816 | |||
| f2f8aed23a | |||
| 68ed52b347 | |||
| 88dac53ae0 | |||
| f84cdff883 | |||
| f869c9425a | |||
| 76ddd5703e | |||
| 669808730e | |||
| e6d9809da5 | |||
| 502032e514 | |||
| 6e9d42f892 | |||
| 70ca27486e | |||
| 12fa434e0f | |||
| efc1e764d2 | |||
| c5c0122243 | |||
| 348b7b8c24 | |||
| e243ad949a | |||
| 9b673f23ad | |||
| 624a0c80ba | |||
| 1064b69d04 | |||
| e9b347acb6 | |||
| 071c8da2aa | |||
| d6538812bf | |||
| 3aeddff736 | |||
| c88c1b125d | |||
| 1513ba9878 | |||
| bc0289c3f4 | |||
| 92c71a374d | |||
| e0547474b8 | |||
| 3e6d21afbb | |||
| d3df01c1c4 | |||
| cdca6fa45c | |||
| 1dde9d8f29 | |||
| 7f0f17b6e0 | |||
| 3507104121 | |||
| 87de634e30 | |||
| 6fa224fa7b | |||
| 8a68d864e6 | |||
| 49f240ea07 | |||
| 3acbd7447a | |||
| 6f126e6daa | |||
| 2adb9d9158 | |||
| 1668dca255 | |||
| 501ff6fff4 | |||
| 3845627fe5 | |||
| cdae03bd54 | |||
| 7b501b71b5 | |||
| 913738a4d1 | |||
| 3312a38c7c | |||
| 71b57fd42c | |||
| 453afbab41 | |||
| 57af5a6d59 | |||
| 0737b2a483 | |||
| eae96d6403 | |||
| b55b8e879e | |||
| 1ed114b80d | |||
| eb8e522bf2 | |||
| 16b71ba6f4 | |||
| 9725f3c6a3 | |||
| 8653a2663f | |||
| ab2cd5dc93 | |||
| 724804a78a | |||
| 77a128e0f7 | |||
| 5343e1054d | |||
| 3930ec0477 | |||
| 996744d7ec | |||
| c00b32017b | |||
| 457014d577 | |||
| be4cbf1ad6 | |||
| aeb008c684 | |||
| 53012dbce1 | |||
| 4fe57f9562 | |||
| 3ba2e420ea | |||
| c81a139c97 | |||
| 66ea684a90 | |||
| a26a6a56d0 | |||
| ee226ea426 | |||
| 76a0290290 | |||
| 85dc24c4a6 | |||
| 1804e380a0 | |||
| 7567f4f3b2 | |||
| e8160eb9df | |||
| 276648630e | |||
| e946a5f071 | |||
| 932401c26d | |||
| fc67d6a18b | |||
| 60908c8bf3 | |||
| b8e3324acb | |||
| 0fc8183799 | |||
| 9a6aa8d15e | |||
| 0b9051cab2 | |||
| 0d7e4db661 | |||
| d5c833b344 | |||
| c875ae7a0e | |||
| c0ec5ef28e | |||
| be91de9535 | |||
| 3088c4fe6d | |||
| 9bd0cd23e7 | |||
| 29f8a698b4 | |||
| 41d274177a | |||
| 7ea249f723 | |||
| 3949be3706 | |||
| 0e932f24cc | |||
| 60225b733b | |||
| 15f99495a1 | |||
| 7b26527e95 | |||
| 386201b6e9 | |||
| 8d278077b1 | |||
| 57c16d2ede | |||
| 2f1613e306 | |||
| 2776c87026 | |||
| 8cc33f5fbb | |||
| 90f91a234a | |||
| fcd1cdf70b | |||
| e265038547 | |||
| a357e0b0d4 | |||
| 2c5e3c6aa5 | |||
| aeda0a0d94 | |||
| ebbcba1b16 | |||
| 4bce10803e | |||
| daee91904d | |||
| c397384766 | |||
| 667efc2feb | |||
| f4469fc53d | |||
| c86c5800a7 | |||
| e4d843ad3a | |||
| 6667bce3d7 | |||
| 50b07da188 | |||
| 708db75760 | |||
| b632ce77ba | |||
| b2c3dd894c | |||
| 9b21bfb53b | |||
| d3adac8a59 | |||
| 5183037a99 | |||
| 2e2bee4fa3 | |||
| 047ccc5f16 | |||
| 59b0d15915 | |||
| 6d961cea78 | |||
| 33f360c9cf | |||
| d3516b4fc9 | |||
| df85d30553 | |||
| d57fca474f | |||
| 69f5151310 | |||
| c47ee68b3f | |||
| 2c06c5e38b | |||
| e6e24ca19f | |||
| 390f60e0ce | |||
| 580e407b05 | |||
| 26bd476c2c | |||
| b8b67fce3d | |||
| 68ed39fc45 | |||
| aeecfabbbc | |||
| 51740e2b9e | |||
| 402abb647c | |||
| 3aa7bf1877 | |||
| ae1dc5841e | |||
| 4b60d65203 | |||
| 3498baad9b | |||
| 0649a141dd | |||
| 65e5905011 | |||
| bf64275aa9 | |||
| 08b400debf | |||
| 9ad2a6cf2e | |||
| 8009f410a4 | |||
| 584fb115b6 | |||
| 70698a4a1a | |||
| eb26d23363 | |||
| c1625c53f4 | |||
| fb55f42d0e | |||
| e4ef35092f | |||
| cfafba589b | |||
| f6367fa89d | |||
| d199209772 | |||
| 4cf5c6a5bf | |||
| 30c3a890ee | |||
| 130ac980fe | |||
| 923cf70c06 | |||
| c7465e1204 | |||
| 4518b59417 | |||
| 0b57f7efe7 | |||
| 7bf18a744c | |||
| fa20763c07 | |||
| 774f3d9e83 | |||
| 2d18ff4ba3 | |||
| 30b068fcdb | |||
| 3d8ce4e7d8 | |||
| e0ab4106fa | |||
| 2c143a8be5 | |||
| 0aa6e4063b | |||
| ec39f099ca | |||
| 4dcc05e796 | |||
| 2af95ec82e | |||
| bbdb521333 | |||
| 56987bc96a | |||
| 8498864dde | |||
| 14710dec90 | |||
| d14177dbca | |||
| 42580bbe2a | |||
| 0c8e036de8 | |||
| a55338d8e3 | |||
| 5d240f85a6 | |||
| cceefa6375 | |||
| 632ed7c089 | |||
| c1528f5501 | |||
| 6c5d952c44 | |||
| 208ad9d615 | |||
| 876aad853c | |||
| 1baa65cc95 | |||
| e01e096188 | |||
| 78ce3bdb99 | |||
| bd348abf32 | |||
| 168369d897 | |||
| 8b04939430 | |||
| 8a8074ee6e | |||
| 5686677383 | |||
| bd4ab2aa04 | |||
| 8ce7dd0d95 | |||
| ca24c4f211 | |||
| 016ab9c5fe | |||
| 8072beb007 | |||
| 99bcffe77c | |||
| 5c7e8bba0d | |||
| 3460967e3b | |||
| 467cd8d978 | |||
| 50cc5ca270 | |||
| 256538e1f9 | |||
| 976a9073f8 | |||
| aa44d5fd43 | |||
| f745470abe | |||
| 04771b1f81 | |||
| 9969cbe11a | |||
| 48dbbfaa24 | |||
| d77711da7b | |||
| 57e7acf97a | |||
| 32aad5eecc | |||
| ecbfcc28b5 | |||
| 2b0b678e25 | |||
| 6a086395be | |||
| 2458996ee7 | |||
| 7a15e645a7 | |||
| 73626da5be | |||
| c680427b1e | |||
| 64cc514171 | |||
| 978e7cfac4 | |||
| 9c790f8cd6 | |||
| 7eb16e51bb | |||
| bb6e850548 | |||
| b7f63ea571 | |||
| ac37a017bb | |||
| 29b1c41064 | |||
| 0f6be5ead7 | |||
| 2d171dd664 | |||
| acb5cc90a9 | |||
| 95ba865cab | |||
| 5b420e0799 | |||
| cbc937884e | |||
| 60ddd151a6 | |||
| a7ca6d23d7 | |||
| d3c085c300 | |||
| f2443fbde0 | |||
| 22d0fe596a | |||
| 4b99ce2fb6 | |||
| f8747d8707 | |||
| a0ea0f7f31 | |||
| 5861602f23 | |||
| 8550f3141c | |||
| 8d8928438d | |||
| 28b1b8e2cc | |||
| 9a55ff221a | |||
| 8f61575579 | |||
| 23fdec541d | |||
| 4137b7f057 | |||
| 6c71a16e3e | |||
| 6511d652f2 | |||
| 39af340fbc | |||
| 63a7231254 | |||
| d97fd3fc22 | |||
| 893e435023 | |||
| 17f1dc8647 | |||
| b8f20add66 | |||
| 304e1a5fb0 | |||
| 82c03ecb33 | |||
| 3d8871abe1 | |||
| 2aecf6e8a1 | |||
| 2a3206d951 | |||
| f80709ae41 | |||
| 33302ef318 | |||
| 9ce5f6e67e | |||
| 7fb9ebbce0 | |||
| dceb83e618 | |||
| fb1ac71f42 | |||
| 54aacff26e | |||
| ae270008b0 | |||
| 22af1edb1d | |||
| ac35a859e0 | |||
| cc27da50df | |||
| 6787cfff55 | |||
| 36154b25ac | |||
| d12ad4f60d | |||
| 86061cb74e | |||
| 32d6b7124c | |||
| c039045c14 | |||
| e6fe42c0ca | |||
| 672d68a73f | |||
| 1b8559f0ef | |||
| 4ad33a3082 | |||
| d2aacea8c5 | |||
| 6c151e21b0 | |||
| 4625efecfd | |||
| c58c8911fe | |||
| 1f6b3e232d | |||
| 90d18c7520 | |||
| ea3ca2751c | |||
| 90b504d3a4 | |||
| 3a4017cea5 | |||
| 14718a08d3 | |||
| 1c6c7e5d4f | |||
| 08c8e7e3e6 | |||
| d055e9dc94 | |||
| a6f0200255 | |||
| 61efb96fe2 | |||
| 1937d727bb | |||
| 4fb2dea1b4 | |||
| f705d82aee | |||
| 71ff481f6c | |||
| 5300e2ceec | |||
| 9c766ec61e | |||
| 3355c3a4c2 | |||
| ec91bac8a5 | |||
| e523a6f60a | |||
| 90e5a5d08b | |||
| 85f3dfbf1d | |||
| 151f681954 | |||
| 7bd67765aa | |||
| f36289786e | |||
| ffc50ceafb | |||
| 0c67ce6476 | |||
| a4f1e048e9 | |||
| 041fe99e01 | |||
| 4f70bea808 | |||
| 6939b216a9 | |||
| ce2073832b | |||
| 08e2adab50 | |||
| b675c4c1bd | |||
| ae32a2e7ae | |||
| 2f538e79fe | |||
| ebe1d712c9 | |||
| 5b82ed8e45 | |||
| 300f8e382b | |||
| 45920f763c | |||
| 0174deb08a | |||
| 9ac16f8e80 | |||
| df6bd58b1a | |||
| 9c995830e2 | |||
| dc1914b9ed | |||
| b385b461e0 | |||
| c64d451287 | |||
| 80ccd119ff | |||
| 18ecece348 | |||
| 18c5fb6add | |||
| daceaa5492 | |||
| f5e060051e | |||
| 975ed41d14 | |||
| cbdfcbcc14 | |||
| 8309535bbe | |||
| 2bf721867b | |||
| 8eca419138 | |||
| 596a4882bc | |||
| 97e72550da | |||
| 4f0aebc32f | |||
| 9365541c8f | |||
| 16291bd1af | |||
| 574a864dd0 | |||
| bde07f6c12 | |||
| 42716b24c9 | |||
| ecca17dbef | |||
| 7e01ead6ff | |||
| 6b834ca6d1 | |||
| 8d12db7dbe | |||
| 4cda75df11 | |||
| 9e899f5974 | |||
| 4aa6f75ea7 | |||
| e9ab6f3f96 | |||
| 633df5f376 | |||
| 74e2907f09 | |||
| 3098d75d01 | |||
| b80888a7bb | |||
| afad0d99cb | |||
| 55aa6eb273 | |||
| 7424a681d6 | |||
| 53f0996fd1 | |||
| 998b913fc9 | |||
| 2f18989f25 | |||
| 80b64cf21e | |||
| 42e55c1db6 | |||
| 00812e7a73 | |||
| 1430aefdf3 | |||
| 46097000a8 | |||
| 5c93bf663a | |||
| 32ac46d9a0 | |||
| 52651c5f2c | |||
| 603d9d2b06 | |||
| 380b7a3699 | |||
| 67f7b3e436 | |||
| 6a883bde96 | |||
| 9603baeb0a | |||
| 6d5549fc8e | |||
| 08ce270e06 | |||
| cf8b3975c0 | |||
| 6f4bfc0e10 | |||
| 3fb952e3c2 | |||
| ceeca9d40f | |||
| 3b89e216cc | |||
| 072d9c59cc | |||
| 9a415738d9 | |||
| d7fda480fd | |||
| 7ff232c814 | |||
| a05b93975e | |||
| c178824a0a | |||
| 3cdf77b85c | |||
| 7a3986af33 | |||
| 0048c92cf5 | |||
| 6b33895f75 | |||
| 577d1965cb | |||
| bfaf4e83bb | |||
| 8f2ba5cd50 | |||
| cd05d5d84a | |||
| 1ff32fe101 | |||
| 3cad70dddd | |||
| 3cbf7b13eb |
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: krgamestudios
|
||||
ko_fi: krgamestudios
|
||||
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
---
|
||||
|
||||
## Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
## To Reproduce
|
||||
|
||||
Steps to reproduce the behaviour:
|
||||
|
||||
1. run `git pull` on the repository
|
||||
2. run `make rebuild` on the code
|
||||
3. ...
|
||||
|
||||
You can include some screenshots here if you'd like!
|
||||
|
||||
## Versioning
|
||||
|
||||
- OS: [for example MacOS, Windows, iOS, Android]
|
||||
- Version: [What version of Toy was this running?]
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
### Describe the feature you’d like
|
||||
|
||||
A clear and concise description of what you’d like to be able to do with Toy.
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
A clear and concise description of any alternative solutions or workarounds you've considered.
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the feature request here.
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Comprehensive Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "dev" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install valgrind
|
||||
run: sudo apt install valgrind
|
||||
- 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
|
||||
@@ -0,0 +1,34 @@
|
||||
#Editor generated files
|
||||
*.suo
|
||||
*.ncb
|
||||
*.user
|
||||
compile_commands.json
|
||||
|
||||
#Directories
|
||||
Release/
|
||||
Debug/
|
||||
Out/
|
||||
release/
|
||||
debug/
|
||||
out/
|
||||
bin/
|
||||
.cache/
|
||||
.vs/
|
||||
|
||||
#Project generated files
|
||||
*.db
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.dll
|
||||
*.exe
|
||||
*.meta
|
||||
*.log
|
||||
*.out
|
||||
*.stackdump
|
||||
*.tb
|
||||
*.filters
|
||||
|
||||
#Shell files
|
||||
*.bat
|
||||
*.sh
|
||||
@@ -0,0 +1,13 @@
|
||||
# License
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -1,4 +1,76 @@
|
||||
This git branch is the (now deprecated) documentation website for The Toy Programming Language version 2.x, which can be found at [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
|
||||
<p align="center">
|
||||
<image src="toylogo.png" />
|
||||
</p>
|
||||
|
||||
Toy v2.x is still under active development, so this documentation will change and evolve over time, and may not reflect the current reference implementation.
|
||||
# Toy
|
||||
|
||||
The Toy programming language is an imperative bytecode-intermediate embedded scripting language. It isn't intended to operate on its own, but rather as part of another program, the "host". This process is intended to allow a decent amount of easy customisation by the host's end user, by exposing logic in script files. Alternatively, binary files in a custom format can be used as well.
|
||||
|
||||
The host will provide all of the extensions needed on a case-by-case basis. Script files have the `.toy` file extension, while binary files have the `.tb` file extension.
|
||||
|
||||
This is the Toy programming language interpreter, written in C.
|
||||
|
||||
# Nifty Features
|
||||
|
||||
* Simple C-like syntax
|
||||
* Bytecode intermediate compilation
|
||||
* Optional, but robust type system (including `opaque` for arbitrary data)
|
||||
* Functions and types are first-class citizens
|
||||
* Import native libraries from the host
|
||||
* Fancy slice notation for strings, arrays and dictionaries
|
||||
* Can re-direct output, error and assertion failure messages
|
||||
* Open source under the zlib license
|
||||
|
||||
## Building
|
||||
|
||||
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
|
||||
|
||||
For Windows(MSVC), Visual Studio project files are included.
|
||||
|
||||
Note: MacOS and Windows(MSVC) are not officially supported, but we'll do our best!
|
||||
|
||||
## Tools
|
||||
|
||||
Run `make install-tools` to install a number of tools, including:
|
||||
|
||||
* VSCode syntax highlighting
|
||||
|
||||
## Syntax
|
||||
|
||||
```
|
||||
import standard; //for a bunch of utility functions
|
||||
|
||||
print "Hello world"; //"print" is a keyword
|
||||
|
||||
var msg = "foobar"; //declare a variable like this
|
||||
|
||||
assert true, "This message won't be seen"; //assert is another keyword
|
||||
|
||||
//-------------------------
|
||||
|
||||
fn makeCounter() { //declare a function like this
|
||||
var total: int = 0; //declare a variable with a type like this
|
||||
|
||||
fn counter(): int { //declare a return type like this
|
||||
return ++total;
|
||||
}
|
||||
|
||||
return counter; //closures are explicitly supported
|
||||
}
|
||||
|
||||
var tally = makeCounter();
|
||||
|
||||
print tally(); //1
|
||||
print tally(); //2
|
||||
print tally(); //3
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
|
||||
|
||||
# Patrons via Patreon
|
||||
|
||||
* Seth A. Robinson
|
||||
|
||||
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
||||
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{97F823E5-3AB8-47EF-B142-C15DD7CADF76}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IgnoreImportLibrary>false</IgnoreImportLibrary>
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Link>
|
||||
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)/source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)out\$(Configuration)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="repl\lib_about.c" />
|
||||
<ClCompile Include="repl\lib_random.c" />
|
||||
<ClCompile Include="repl\lib_runner.c" />
|
||||
<ClCompile Include="repl\lib_standard.c" />
|
||||
<ClCompile Include="repl\repl_main.c" />
|
||||
<ClCompile Include="repl\repl_tools.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="repl\lib_about.h" />
|
||||
<ClInclude Include="repl\lib_random.h" />
|
||||
<ClInclude Include="repl\lib_runner.h" />
|
||||
<ClInclude Include="repl\lib_standard.h" />
|
||||
<ClInclude Include="repl\repl_tools.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="Toy.vcxproj">
|
||||
<Project>{26360002-cc2a-469a-9b28-ba0c1af41657}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{26360002-CC2A-469A-9B28-BA0C1AF41657}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)out\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TOY_EXPORTS;TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;TOY_EXPORTS;TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="source\toy_ast_node.c" />
|
||||
<ClCompile Include="source\toy_builtin.c" />
|
||||
<ClCompile Include="source\toy_common.c" />
|
||||
<ClCompile Include="source\toy_compiler.c" />
|
||||
<ClCompile Include="source\toy_drive_system.c" />
|
||||
<ClCompile Include="source\toy_interpreter.c" />
|
||||
<ClCompile Include="source\toy_keyword_types.c" />
|
||||
<ClCompile Include="source\toy_lexer.c" />
|
||||
<ClCompile Include="source\toy_literal.c" />
|
||||
<ClCompile Include="source\toy_literal_array.c" />
|
||||
<ClCompile Include="source\toy_literal_dictionary.c" />
|
||||
<ClCompile Include="source\toy_memory.c" />
|
||||
<ClCompile Include="source\toy_parser.c" />
|
||||
<ClCompile Include="source\toy_reffunction.c" />
|
||||
<ClCompile Include="source\toy_refstring.c" />
|
||||
<ClCompile Include="source\toy_scope.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="source\toy.h" />
|
||||
<ClInclude Include="source\toy_ast_node.h" />
|
||||
<ClInclude Include="source\toy_builtin.h" />
|
||||
<ClInclude Include="source\toy_common.h" />
|
||||
<ClInclude Include="source\toy_compiler.h" />
|
||||
<ClInclude Include="source\toy_console_colors.h" />
|
||||
<ClInclude Include="source\toy_drive_system.h" />
|
||||
<ClInclude Include="source\toy_interpreter.h" />
|
||||
<ClInclude Include="source\toy_keyword_types.h" />
|
||||
<ClInclude Include="source\toy_lexer.h" />
|
||||
<ClInclude Include="source\toy_literal.h" />
|
||||
<ClInclude Include="source\toy_literal_array.h" />
|
||||
<ClInclude Include="source\toy_literal_dictionary.h" />
|
||||
<ClInclude Include="source\toy_memory.h" />
|
||||
<ClInclude Include="source\toy_opcodes.h" />
|
||||
<ClInclude Include="source\toy_parser.h" />
|
||||
<ClInclude Include="source\toy_reffunction.h" />
|
||||
<ClInclude Include="source\toy_refstring.h" />
|
||||
<ClInclude Include="source\toy_scope.h" />
|
||||
<ClInclude Include="source\toy_token_types.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33213.308
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Toy", "Toy.vcxproj", "{26360002-CC2A-469A-9B28-BA0C1AF41657}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Repl", "Repl.vcxproj", "{97F823E5-3AB8-47EF-B142-C15DD7CADF76}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657} = {26360002-CC2A-469A-9B28-BA0C1AF41657}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.Build.0 = Debug|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.Build.0 = Debug|Win32
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.ActiveCfg = Release|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.Build.0 = Release|x64
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.ActiveCfg = Release|Win32
|
||||
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.Build.0 = Release|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.Build.0 = Debug|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.Build.0 = Debug|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.ActiveCfg = Release|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.Build.0 = Release|x64
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.ActiveCfg = Release|Win32
|
||||
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7089F1AD-8EC0-4F27-AFD1-5FD43D91AABC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,4 +0,0 @@
|
||||
title: The Toy Programming Language
|
||||
description: Documentation For The Toy Programming Language
|
||||
keywords: programming,coding
|
||||
author: Kayne Ruse (Ratstail91)
|
||||
@@ -1,8 +0,0 @@
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-WQ8Q1JV8E8"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-WQ8Q1JV8E8');
|
||||
</script>
|
||||
@@ -1,21 +0,0 @@
|
||||
<!-- site information -->
|
||||
<meta name="description" content="{{ site.description }}" />
|
||||
<meta name="author" content="{{ site.author }}" />
|
||||
<meta name="keywords" content="{{ site.keywords }}" />
|
||||
|
||||
<!-- facebook -->
|
||||
<meta property="og:url" content="{{ site.url }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="{{ site.baseurl }}/assets/repo-preview.png" />
|
||||
<meta property="og:title" content="{{ page.title }}" />
|
||||
<meta property="og:description" content="{{ page.description }}" />
|
||||
|
||||
<!-- twitter -->
|
||||
<meta name="twitter:card" content="{{ site.title }}" />
|
||||
<meta name="twitter:url" content="{{ site.url}}" />
|
||||
<meta name="twitter:type" content="website" />
|
||||
<meta name="twitter:image" content="{{ site.baseurl }}/assets/repo-preview.png" />
|
||||
<meta name="twitter:title" content="{{ page.title }}" />
|
||||
<meta name="twitter:description" content="{{ page.description }}" />
|
||||
|
||||
<link rel="icon" href="{{ site.baseurl }}/favicon.png">
|
||||
@@ -1,21 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<!-- device settings -->
|
||||
<meta charset = "UTF-8" />
|
||||
<meta name="Content-Type" content="text/html" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- page title -->
|
||||
<title>{{ page.title }}</title>
|
||||
|
||||
{% include metadata.html %}
|
||||
{% include analytics.html %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{ content }}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 454 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,18 +0,0 @@
|
||||
---
|
||||
layout: page
|
||||
title: The Toy Programming Language
|
||||
---
|
||||
|
||||
<div style="justify-self: center;">
|
||||
<image src="assets/toylogo.png" width="250" height="250" />
|
||||
</div>
|
||||
|
||||
<div style="justify-self: center;">
|
||||
<a href="https://github.com/krgamestudios/Toy"><img src="https://github.com/krgamestudios/Toy/actions/workflows/continuous-integration-v2.yml/badge.svg"></a>
|
||||
</div>
|
||||
|
||||
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through text files.
|
||||
|
||||
The documentation on this website is under construction, for further information, see the repository: [https://gitea.krgamestudios.com/krgamestudios/Toy](https://gitea.krgamestudios.com/krgamestudios/Toy), or the GitHub mirror: [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
|
||||
|
||||
An example of Toy in action: [Vampire Toyvivors](https://gitea.krgamestudios.com/krgamestudios/VampireToyvivors).
|
||||
@@ -0,0 +1,90 @@
|
||||
export CFLAGS+=-std=c18 -pedantic -Werror
|
||||
|
||||
export TOY_OUTDIR = out
|
||||
|
||||
all: $(TOY_OUTDIR) repl
|
||||
|
||||
#repl builds
|
||||
repl: $(TOY_OUTDIR) library
|
||||
$(MAKE) -j8 -C repl
|
||||
|
||||
repl-static: $(TOY_OUTDIR) static
|
||||
$(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: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source library-release
|
||||
|
||||
static-release: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source static-release
|
||||
|
||||
#distribution
|
||||
dist: export CFLAGS+=-O2 -mtune=native -march=native
|
||||
dist: repl-release
|
||||
|
||||
#utils
|
||||
test: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -C test
|
||||
|
||||
test-sanitized: export CFLAGS+=-fsanitize=address,undefined
|
||||
test-sanitized: export LIBS+=-static-libasan
|
||||
test-sanitized: export DISABLE_VALGRIND=true
|
||||
test-sanitized: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -C test
|
||||
|
||||
$(TOY_OUTDIR):
|
||||
mkdir $(TOY_OUTDIR)
|
||||
|
||||
#utils
|
||||
install-tools:
|
||||
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
||||
find . -empty -type d -delete
|
||||
else ifeq ($(shell uname),Linux)
|
||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
||||
rm -rf out
|
||||
find . -empty -type d -delete
|
||||
else ifeq ($(OS),Windows_NT)
|
||||
$(RM) *.o *.a *.exe
|
||||
else ifeq ($(shell uname),Darwin)
|
||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.dylib' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
||||
rm -rf out
|
||||
find . -empty -type d -delete
|
||||
else
|
||||
@echo "Deletion failed - what platform is this?"
|
||||
endif
|
||||
|
||||
rebuild: clean all
|
||||
|
Before Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,162 @@
|
||||
#include "lib_about.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//the about keys
|
||||
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
|
||||
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
|
||||
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
|
||||
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
|
||||
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
|
||||
|
||||
//the about identifiers
|
||||
Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
|
||||
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
|
||||
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
|
||||
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
|
||||
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
|
||||
|
||||
//the about values
|
||||
Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
|
||||
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
|
||||
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
|
||||
Toy_Literal buildLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(TOY_VERSION_BUILD));
|
||||
Toy_Literal authorLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("Kayne Ruse, KR Game Studios"));
|
||||
|
||||
//store as an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with values
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//set each key/value pair
|
||||
Toy_setLiteralDictionary(dictionary, majorKeyLiteral, majorLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, minorKeyLiteral, minorLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, patchKeyLiteral, patchLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, buildKeyLiteral, buildLiteral);
|
||||
Toy_setLiteralDictionary(dictionary, authorKeyLiteral, authorLiteral);
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal anyType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, anyType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
}
|
||||
|
||||
//store globally
|
||||
else {
|
||||
//make sure the names aren't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
|
||||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal intType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_INTEGER, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
|
||||
//major
|
||||
Toy_declareScopeVariable(interpreter->scope, majorIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, majorIdentifierLiteral, majorLiteral, false);
|
||||
|
||||
//minor
|
||||
Toy_declareScopeVariable(interpreter->scope, minorIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, minorIdentifierLiteral, minorLiteral, false);
|
||||
|
||||
//patch
|
||||
Toy_declareScopeVariable(interpreter->scope, patchIdentifierLiteral, intType);
|
||||
Toy_setScopeVariable(interpreter->scope, patchIdentifierLiteral, patchLiteral, false);
|
||||
|
||||
//build
|
||||
Toy_declareScopeVariable(interpreter->scope, buildIdentifierLiteral, strType);
|
||||
Toy_setScopeVariable(interpreter->scope, buildIdentifierLiteral, buildLiteral, false);
|
||||
|
||||
//author
|
||||
Toy_declareScopeVariable(interpreter->scope, authorIdentifierLiteral, strType);
|
||||
Toy_setScopeVariable(interpreter->scope, authorIdentifierLiteral, authorLiteral, false);
|
||||
|
||||
Toy_freeLiteral(intType);
|
||||
Toy_freeLiteral(strType);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(majorKeyLiteral);
|
||||
Toy_freeLiteral(minorKeyLiteral);
|
||||
Toy_freeLiteral(patchKeyLiteral);
|
||||
Toy_freeLiteral(buildKeyLiteral);
|
||||
Toy_freeLiteral(authorKeyLiteral);
|
||||
|
||||
Toy_freeLiteral(majorIdentifierLiteral);
|
||||
Toy_freeLiteral(minorIdentifierLiteral);
|
||||
Toy_freeLiteral(patchIdentifierLiteral);
|
||||
Toy_freeLiteral(buildIdentifierLiteral);
|
||||
Toy_freeLiteral(authorIdentifierLiteral);
|
||||
|
||||
Toy_freeLiteral(majorLiteral);
|
||||
Toy_freeLiteral(minorLiteral);
|
||||
Toy_freeLiteral(patchLiteral);
|
||||
Toy_freeLiteral(buildLiteral);
|
||||
Toy_freeLiteral(authorLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
#include "lib_random.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
static int hashInt(int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
typedef struct Toy_RandomGenerator {
|
||||
int seed; //mutated with each call
|
||||
} Toy_RandomGenerator;
|
||||
|
||||
//Toy native functions
|
||||
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the seed argument
|
||||
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal seedLiteralIdn = seedLiteral;
|
||||
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
|
||||
Toy_freeLiteral(seedLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(seedLiteral)) {
|
||||
Toy_freeLiteral(seedLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(seedLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
|
||||
Toy_freeLiteral(seedLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//generate the generator object
|
||||
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
|
||||
generator->seed = TOY_AS_INTEGER(seedLiteral);
|
||||
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
|
||||
|
||||
//return and cleanup
|
||||
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
|
||||
|
||||
Toy_freeLiteral(seedLiteral);
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||
|
||||
//generate the new value and package up the return
|
||||
generator->seed = hashInt(generator->seed);
|
||||
|
||||
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
Toy_freeLiteral(resultLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(generatorLiteral)) {
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||
|
||||
//clear out the runner object
|
||||
TOY_FREE(Toy_RandomGenerator, generator);
|
||||
Toy_freeLiteral(generatorLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
const char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"createRandomGenerator", nativeCreateRandomGenerator},
|
||||
{"generateRandomNumber", nativeGenerateRandomNumber},
|
||||
{"freeRandomGenerator", nativeFreeRandomGenerator},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
#define TOY_OPAQUE_TAG_RANDOM 200
|
||||
@@ -0,0 +1,553 @@
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
#include "toy_drive_system.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
#include "repl_tools.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct Toy_Runner {
|
||||
Toy_Interpreter interpreter;
|
||||
const unsigned char* bytecode;
|
||||
size_t size;
|
||||
|
||||
bool dirty;
|
||||
} Toy_Runner;
|
||||
|
||||
//Toy native functions
|
||||
static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to loadScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the file path literal with a handle
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
|
||||
|
||||
if (TOY_IS_NULL(filePathLiteral)) {
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//use raw types - easier
|
||||
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
|
||||
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
|
||||
|
||||
//load and compile the bytecode
|
||||
size_t fileSize = 0;
|
||||
const char* source = (const char*)Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!source) {
|
||||
interpreter->errorOutput("Failed to load source file\n");
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const unsigned char* bytecode = Toy_compileString(source, &fileSize);
|
||||
free((void*)source);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to compile source file\n");
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//build the runner object
|
||||
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
|
||||
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
|
||||
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
|
||||
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
|
||||
runner->interpreter.hooks = interpreter->hooks;
|
||||
runner->interpreter.scope = NULL;
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->bytecode = bytecode;
|
||||
runner->size = fileSize;
|
||||
runner->dirty = false;
|
||||
|
||||
//build the opaque object, and push it to the stack
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
//free the drive path
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to loadScriptBytecode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the argument
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteralIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(drivePathLiteral)) {
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
|
||||
|
||||
if (TOY_IS_NULL(filePathLiteral)) {
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//use raw types - easier
|
||||
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
|
||||
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
|
||||
|
||||
//load the bytecode
|
||||
size_t fileSize = 0;
|
||||
unsigned char* bytecode = (unsigned char*)Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to load bytecode file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//build the runner object
|
||||
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
|
||||
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
|
||||
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
|
||||
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
|
||||
runner->interpreter.hooks = interpreter->hooks;
|
||||
runner->interpreter.scope = NULL;
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->bytecode = bytecode;
|
||||
runner->size = fileSize;
|
||||
runner->dirty = false;
|
||||
|
||||
//build the opaque object, and push it to the stack
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
//free the drive path
|
||||
Toy_freeLiteral(filePathLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//run
|
||||
if (runner->dirty) {
|
||||
interpreter->errorOutput("Can't re-run a dirty script (try resetting it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* bytecodeCopy = TOY_ALLOCATE(unsigned char, runner->size);
|
||||
memcpy(bytecodeCopy, runner->bytecode, runner->size); //need a COPY of the bytecode, because the interpreter eats it
|
||||
|
||||
Toy_runInterpreter(&runner->interpreter, bytecodeCopy, runner->size);
|
||||
runner->dirty = true;
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to getScriptVar\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal varName = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal varNameIdn = varName;
|
||||
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
|
||||
Toy_freeLiteral(varNameIdn);
|
||||
}
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in getScriptVar\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//dirty check
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't access variable from a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the desired variable
|
||||
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &result);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count < 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to callScriptFn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the rest args
|
||||
Toy_LiteralArray tmp;
|
||||
Toy_initLiteralArray(&tmp);
|
||||
|
||||
while (arguments->count > 2) {
|
||||
Toy_Literal lit = Toy_popLiteralArray(arguments);
|
||||
Toy_pushLiteralArray(&tmp, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
Toy_LiteralArray rest;
|
||||
Toy_initLiteralArray(&rest);
|
||||
|
||||
while (tmp.count > 0) { //correct the order of the rest args
|
||||
Toy_Literal lit = Toy_popLiteralArray(&tmp);
|
||||
Toy_pushLiteralArray(&rest, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
Toy_freeLiteralArray(&tmp);
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal varName = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal varNameIdn = varName;
|
||||
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
|
||||
Toy_freeLiteral(varNameIdn);
|
||||
}
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(varName) || TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in callScriptFn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//dirty check
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't access fn from a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the desired variable
|
||||
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
|
||||
Toy_Literal fn = TOY_TO_NULL_LITERAL;
|
||||
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &fn);
|
||||
|
||||
if (!TOY_IS_FUNCTION(fn)) {
|
||||
interpreter->errorOutput("Can't run a non-function literal\n");
|
||||
Toy_freeLiteral(fn);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
}
|
||||
|
||||
//call
|
||||
Toy_LiteralArray resultArray;
|
||||
Toy_initLiteralArray(&resultArray);
|
||||
|
||||
Toy_callLiteralFn(interpreter, fn, &rest, &resultArray);
|
||||
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
if (resultArray.count > 0) {
|
||||
result = Toy_popLiteralArray(&resultArray);
|
||||
}
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteralArray(&resultArray);
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(fn);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to resetScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in resetScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//reset
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't reset a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->dirty = false;
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to freeScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in freeScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//clear out the runner object
|
||||
runner->interpreter.hooks = NULL;
|
||||
Toy_freeInterpreter(&runner->interpreter);
|
||||
TOY_FREE_ARRAY(unsigned char, runner->bytecode, runner->size);
|
||||
|
||||
TOY_FREE(Toy_Runner, runner);
|
||||
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to checkScriptDirty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in checkScriptDirty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//run
|
||||
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(runner->dirty);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
const char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"loadScript", nativeLoadScript},
|
||||
{"loadScriptBytecode", nativeLoadScriptBytecode},
|
||||
{"runScript", nativeRunScript},
|
||||
{"getScriptVar", nativeGetScriptVar},
|
||||
{"callScriptFn", nativeCallScriptFn},
|
||||
{"resetScript", nativeResetScript},
|
||||
{"freeScript", nativeFreeScript},
|
||||
{"checkScriptDirty", nativeCheckScriptDirty},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
#define TOY_OPAQUE_TAG_RUNNER 100
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
CC=gcc
|
||||
|
||||
IDIR+=. ../source
|
||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||
LIBS+=-ltoy
|
||||
|
||||
ODIR = obj
|
||||
SRC = $(wildcard *.c)
|
||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||
OUTNAME=toy
|
||||
OUT=../$(TOY_OUTDIR)/toyrepl
|
||||
|
||||
all: $(OBJ)
|
||||
ifeq ($(shell uname),Darwin)
|
||||
cp $(PWD)/$(TOY_OUTDIR)/lib$(OUTNAME).dylib /usr/local/lib/
|
||||
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) $(LIBS)
|
||||
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):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) $(ODIR)
|
||||
rm /usr/local/lib/lib$(OUTNAME).dylib
|
||||
@@ -0,0 +1,230 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_about.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_random.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "toy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define INPUT_BUFFER_SIZE 2048
|
||||
|
||||
void repl(const char* initialInput) {
|
||||
//repl does it's own thing for now
|
||||
bool error = false;
|
||||
|
||||
char input[INPUT_BUFFER_SIZE];
|
||||
memset(input, 0, INPUT_BUFFER_SIZE);
|
||||
|
||||
Toy_Interpreter interpreter; //persist the interpreter for the scopes
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
for(;;) {
|
||||
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))) {
|
||||
break;
|
||||
}
|
||||
|
||||
//setup this iteration
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler 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
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//pack up and restart
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
|
||||
}
|
||||
error = true;
|
||||
Toy_freeASTNode(node);
|
||||
break;
|
||||
}
|
||||
|
||||
Toy_writeCompiler(&compiler, node);
|
||||
Toy_freeASTNode(node);
|
||||
node = Toy_scanParser(&parser);
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
//get the bytecode dump
|
||||
size_t size = 0;
|
||||
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
||||
|
||||
//run the bytecode
|
||||
Toy_runInterpreter(&interpreter, tb, size);
|
||||
}
|
||||
|
||||
//clean up this iteration
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
error = false;
|
||||
|
||||
if (initialInput) {
|
||||
free((void*)initialInput);
|
||||
initialInput = NULL;
|
||||
|
||||
if (interpreter.panic) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
//entry point
|
||||
int main(int argc, const char* argv[]) {
|
||||
Toy_initCommandLine(argc, argv);
|
||||
|
||||
//setup the drive system (for filesystem access)
|
||||
Toy_initDriveSystem();
|
||||
Toy_setDrivePath("scripts", "scripts");
|
||||
|
||||
//command line specific actions
|
||||
if (Toy_commandLine.error) {
|
||||
Toy_usageCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Toy_commandLine.help) {
|
||||
Toy_helpCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Toy_commandLine.version) {
|
||||
Toy_copyrightCommandLine(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//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 (Toy_commandLine.sourcefile) {
|
||||
//only works on toy files
|
||||
const char* s = strrchr(Toy_commandLine.sourcefile, '.');
|
||||
if (!s || strcmp(s, ".toy")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//run the source file
|
||||
Toy_runSourceFile(Toy_commandLine.sourcefile);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run from stdin
|
||||
if (Toy_commandLine.source) {
|
||||
Toy_runSource(Toy_commandLine.source);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//compile source file
|
||||
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;
|
||||
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;
|
||||
}
|
||||
Toy_writeFile(Toy_commandLine.outfile, tb, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run binary
|
||||
if (Toy_commandLine.binaryfile) {
|
||||
//only works on tb files
|
||||
const char* c = strrchr(Toy_commandLine.binaryfile, '.');
|
||||
if (!c || strcmp(c, ".tb")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.tb', found '%s')" TOY_CC_RESET, argv[0], c); //this one is never seen
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Toy_commandLine.parseBytecodeHeader) {
|
||||
//only parse the bytecode header
|
||||
Toy_parseBinaryFileHeader(Toy_commandLine.binaryfile);
|
||||
}
|
||||
else {
|
||||
//run the binary file
|
||||
Toy_runBinaryFile(Toy_commandLine.binaryfile);
|
||||
}
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* initialSource = NULL;
|
||||
if (Toy_commandLine.initialfile) {
|
||||
//only works on toy files
|
||||
const char* s = strrchr(Toy_commandLine.initialfile, '.');
|
||||
if (!s || strcmp(s, ".toy")) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
initialSource = (const char*)Toy_readFile(Toy_commandLine.initialfile, &size);
|
||||
}
|
||||
|
||||
repl(initialSource);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveSystem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_about.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_random.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_console_colors.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
|
||||
const unsigned char* Toy_readFile(const char* path, size_t* fileSize) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
|
||||
if (file == NULL) {
|
||||
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);
|
||||
|
||||
unsigned char* buffer = (unsigned char*)malloc(*fileSize + 1);
|
||||
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t bytesRead = fread(buffer, sizeof(unsigned char), *fileSize, file);
|
||||
|
||||
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||
|
||||
if (bytesRead < *fileSize) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
|
||||
FILE* file = fopen(path, "wb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t written = fwrite(bytes, size, 1, file);
|
||||
|
||||
if (written != 1) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//repl functions
|
||||
const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
|
||||
Toy_initLexer(&lexer, source);
|
||||
Toy_initParser(&parser, &lexer);
|
||||
Toy_initCompiler(&compiler);
|
||||
|
||||
//step 1 - run the parser until the end of the source
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//on error, pack up and leave
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
Toy_freeASTNode(node);
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_writeCompiler(&compiler, node);
|
||||
Toy_freeASTNode(node);
|
||||
node = Toy_scanParser(&parser);
|
||||
}
|
||||
|
||||
//step 2 - get the bytecode dump
|
||||
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
||||
|
||||
//cleanup
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
//no lexer to clean up
|
||||
|
||||
//finally
|
||||
return tb;
|
||||
}
|
||||
|
||||
void Toy_runBinary(const unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
Toy_runInterpreter(&interpreter, tb, (int)size);
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void Toy_runBinaryFile(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
Toy_runBinary(tb, size);
|
||||
//interpreter takes ownership of the binary data
|
||||
}
|
||||
|
||||
void Toy_runSource(const char* source) {
|
||||
size_t size = 0;
|
||||
const unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_runBinary(tb, size);
|
||||
}
|
||||
|
||||
void Toy_runSourceFile(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
Toy_runSource(source);
|
||||
free((void*)source);
|
||||
}
|
||||
|
||||
//utils for debugging the header
|
||||
static unsigned char readByte(const unsigned char* tb, int* count) {
|
||||
unsigned char ret = *(unsigned char*)(tb + *count);
|
||||
*count += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* readString(const unsigned char* tb, int* count) {
|
||||
const unsigned char* ret = tb + *count;
|
||||
*count += strlen((char*)ret) + 1; //+1 for null character
|
||||
return (const char*)ret;
|
||||
}
|
||||
|
||||
void Toy_parseBinaryFileHeader(const char* fname) {
|
||||
size_t size = 0; //not used
|
||||
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||
if (!tb || size < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
//header section
|
||||
const unsigned char major = readByte(tb, &count);
|
||||
const unsigned char minor = readByte(tb, &count);
|
||||
const unsigned char patch = readByte(tb, &count);
|
||||
|
||||
const char* build = readString(tb, &count);
|
||||
|
||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (interpreter built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
|
||||
printf("Toy Programming Language Bytecode Version ");
|
||||
|
||||
//print the output
|
||||
if (major == TOY_VERSION_MAJOR && minor == TOY_VERSION_MINOR && patch == TOY_VERSION_PATCH) {
|
||||
printf("%d.%d.%d", major, minor, patch);
|
||||
}
|
||||
else {
|
||||
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%d.%d.%d" TOY_CC_RESET, major, minor, patch);
|
||||
}
|
||||
|
||||
printf(" (interpreter built on ");
|
||||
|
||||
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD)) == 0) {
|
||||
printf("%s", build);
|
||||
}
|
||||
else {
|
||||
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%s" TOY_CC_RESET, build);
|
||||
}
|
||||
|
||||
printf(")\n");
|
||||
|
||||
//cleanup
|
||||
free((void*)tb);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
const unsigned char* Toy_readFile(const char* path, size_t* fileSize);
|
||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
|
||||
|
||||
const unsigned char* Toy_compileString(const char* source, size_t* size);
|
||||
|
||||
void Toy_runBinary(const unsigned char* tb, size_t size);
|
||||
void Toy_runBinaryFile(const char* fname);
|
||||
void Toy_runSource(const char* source);
|
||||
void Toy_runSourceFile(const char* fname);
|
||||
|
||||
void Toy_parseBinaryFileHeader(const char* fname);
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="454.000000pt" height="454.000000pt" viewBox="0 0 454.000000 454.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,454.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M1178 4533 c-3 -5 -4 -508 -3 -1118 l0 -1111 -565 0 -565 0 -3 -1152
|
||||
-2 -1152 2230 0 2230 0 -2 1152 -3 1152 -522 0 c-359 0 -523 3 -524 10 0 6 0
|
||||
509 0 1119 l-1 1107 -1133 0 c-624 0 -1135 -3 -1137 -7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 730 B |
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
//WARNING: please think twice before using this in a test
|
||||
fn fib(n : int) {
|
||||
if (n < 2) return n;
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
for (var i = 0; i <= 35; i++) {
|
||||
var res = fib(i);
|
||||
print string i + ": " + string res;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
|
||||
How to run this program:
|
||||
|
||||
toyrepl -n -t scripts/level.toy
|
||||
|
||||
How to move around:
|
||||
|
||||
move(up);
|
||||
move(down);
|
||||
move(left);
|
||||
move(right);
|
||||
|
||||
*/
|
||||
|
||||
//constants
|
||||
var WIDTH: int const = 12;
|
||||
var HEIGHT: int const = 12;
|
||||
|
||||
//WIDTH * HEIGHT in size
|
||||
var tiles: [[int]] const = [
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] //BUG: map is twisted along this diagonal
|
||||
];
|
||||
|
||||
var tileset: [int: string] const = [
|
||||
0: " ",
|
||||
1: "X "
|
||||
];
|
||||
|
||||
//variables
|
||||
var posX: int = 4;
|
||||
var posY: int = 4;
|
||||
|
||||
//functions
|
||||
fn draw() {
|
||||
for (var j: int = 0; j < HEIGHT; j++) {
|
||||
for (var i: int = 0; i < WIDTH; i++) {
|
||||
//draw the player pos
|
||||
if (i == posX && j == posY) {
|
||||
print "O ";
|
||||
continue;
|
||||
}
|
||||
|
||||
print tileset[ tiles[i][j] ];
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
fn moveRelative(xrel: int, yrel: int) {
|
||||
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1 || (xrel != 0 && yrel != 0)) {
|
||||
print "too fast!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (tiles[posX + xrel][posY + yrel] > 0) {
|
||||
print "Can't move that way\n";
|
||||
return;
|
||||
}
|
||||
|
||||
posX += xrel;
|
||||
posY += yrel;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
//wrap for easy use
|
||||
var up: [int] const = [0, -1];
|
||||
var down: [int] const = [0, 1];
|
||||
var left: [int] const = [-1, 0];
|
||||
var right: [int] const = [1, 0];
|
||||
|
||||
fn move(dir: [int] const) {
|
||||
return moveRelative(dir[0], dir[1]);
|
||||
}
|
||||
|
||||
//initial display
|
||||
move([0, 0]);
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
|
||||
Since this is a pseudo-random generator, and there's no internal state to the algorithm other
|
||||
than the generator opaque, there needs to be a "call counter" (current depth) to shuffle the
|
||||
initial seeds, otherwise generators created from other generators will resemble their parents,
|
||||
but one call greater.
|
||||
|
||||
*/
|
||||
|
||||
import standard;
|
||||
import random;
|
||||
|
||||
var DEPTH: int const = 20;
|
||||
var levels = [];
|
||||
|
||||
//generate the level seeds
|
||||
var generator: opaque = createRandomGenerator(clock().hash());
|
||||
|
||||
for (var i: int = 0; i < DEPTH; i++) {
|
||||
levels.push(generator.generateRandomNumber());
|
||||
}
|
||||
|
||||
generator.freeRandomGenerator();
|
||||
|
||||
//generate "levels" of a roguelike
|
||||
for (var i = 0; i < DEPTH; i++) {
|
||||
var rng: opaque = createRandomGenerator(levels[i] + i);
|
||||
|
||||
print "---";
|
||||
print levels[i];
|
||||
print rng.generateRandomNumber();
|
||||
print rng.generateRandomNumber();
|
||||
print rng.generateRandomNumber();
|
||||
|
||||
rng.freeRandomGenerator();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//number of iterations
|
||||
var SIZE: int const = 100;
|
||||
|
||||
//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;
|
||||
|
||||
//run
|
||||
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
||||
//left
|
||||
var output = (lookup[" "][prev[0]][prev[1]]);
|
||||
|
||||
//middle
|
||||
for (var i = 1; i < SIZE-1; i++) {
|
||||
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
|
||||
}
|
||||
|
||||
//right
|
||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||
|
||||
print output;
|
||||
prev = output;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
CC=gcc
|
||||
|
||||
IDIR+=.
|
||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||
LIBS+=
|
||||
|
||||
ODIR = obj
|
||||
SRC = $(wildcard *.c)
|
||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||
|
||||
OUTNAME=toy
|
||||
|
||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
||||
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||
else ifeq ($(shell uname),Linux)
|
||||
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
|
||||
CFLAGS += -fPIC
|
||||
else ifeq ($(OS),Windows_NT)
|
||||
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||
else ifeq ($(shell uname),Darwin)
|
||||
LIBLINE = $(OBJ)
|
||||
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).dylib
|
||||
else
|
||||
@echo "Platform test failed - what platform is this?"
|
||||
exit 1
|
||||
endif
|
||||
|
||||
library: $(OBJ)
|
||||
$(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE)
|
||||
|
||||
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):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) $(ODIR)
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
/* toy.h - A Toy Programming Language
|
||||
|
||||
If you're looking how to use Toy directly, try https://toylang.com/
|
||||
Otherwise, these headers may help learn how Toy works internally.
|
||||
|
||||
*/
|
||||
|
||||
/* utilities - these define a bunch of useful macros based on platform.
|
||||
|
||||
The most important one is `TOY_API`, which highlights functions intended for the end user.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_console_colors.h"
|
||||
#include "toy_memory.h"
|
||||
#include "toy_drive_system.h"
|
||||
|
||||
/* core pipeline - from source to execution
|
||||
|
||||
Each step is as follows:
|
||||
|
||||
source -> lexer -> token
|
||||
token -> parser -> AST
|
||||
AST -> compiler -> bytecode
|
||||
bytecode -> interpreter -> result
|
||||
|
||||
I should note that the parser -> compiler phase is actually made up of two steps - the write step
|
||||
and the collate step. See `Toy_compileString()` in `repl/repl_tools.c` for an example of how to compile
|
||||
properly.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_compiler.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
/* building block structures - the basic units of operation
|
||||
|
||||
Literals represent any value within the language, including some internal ones that you never see.
|
||||
Literal Arrays are literally arrays within memory, and are the most heavily used structure in Toy.
|
||||
Literal Dictionaries are unordered key-value hashmaps, that use a running strategy for collisions.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
/* other components - you probably won't use these directly, but they're a good learning opportunity.
|
||||
|
||||
`Toy_Scope` holds the variables of a specific scope within Toy - be it a script, a function, a block, etc.
|
||||
Scopes are also where the type system lives at runtime. They use identifier literals as keys, exclusively.
|
||||
|
||||
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and
|
||||
faster to copy and move. In reality, since strings are considered immutable, multiple variables can point
|
||||
to the same string to save memory, and you can just create a new one of these vars pointing to the original
|
||||
rather than copying entirely for a speed boost. This module has it's own memory allocator system that is
|
||||
plugged into the main memory allocator.
|
||||
|
||||
`Toy_RefFunction` acts similarly to `Toy_RefString`, but instead operates on function bytecode.
|
||||
|
||||
*/
|
||||
|
||||
#include "toy_scope.h"
|
||||
#include "toy_refstring.h"
|
||||
#include "toy_reffunction.h"
|
||||
@@ -0,0 +1,403 @@
|
||||
#include "toy_ast_node.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
|
||||
//don't free a NULL node
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(node->type) {
|
||||
case TOY_AST_NODE_ERROR:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_LITERAL:
|
||||
Toy_freeLiteral(node->atomic.literal);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_UNARY:
|
||||
Toy_freeASTNode(node->unary.child);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BINARY:
|
||||
Toy_freeASTNode(node->binary.left);
|
||||
Toy_freeASTNode(node->binary.right);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_TERNARY:
|
||||
Toy_freeASTNode(node->ternary.condition);
|
||||
Toy_freeASTNode(node->ternary.thenPath);
|
||||
Toy_freeASTNode(node->ternary.elsePath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_GROUPING:
|
||||
Toy_freeASTNode(node->grouping.child);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BLOCK:
|
||||
if (node->block.capacity > 0) {
|
||||
for (int i = 0; i < node->block.count; i++) {
|
||||
freeASTNodeCustom(node->block.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_COMPOUND:
|
||||
if (node->compound.capacity > 0) {
|
||||
for (int i = 0; i < node->compound.count; i++) {
|
||||
freeASTNodeCustom(node->compound.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PAIR:
|
||||
Toy_freeASTNode(node->pair.left);
|
||||
Toy_freeASTNode(node->pair.right);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_INDEX:
|
||||
Toy_freeASTNode(node->index.first);
|
||||
Toy_freeASTNode(node->index.second);
|
||||
Toy_freeASTNode(node->index.third);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_VAR_DECL:
|
||||
Toy_freeLiteral(node->varDecl.identifier);
|
||||
Toy_freeLiteral(node->varDecl.typeLiteral);
|
||||
Toy_freeASTNode(node->varDecl.expression);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_COLLECTION:
|
||||
if (node->fnCollection.capacity > 0) {
|
||||
for (int i = 0; i < node->fnCollection.count; i++) {
|
||||
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_DECL:
|
||||
Toy_freeLiteral(node->fnDecl.identifier);
|
||||
Toy_freeASTNode(node->fnDecl.arguments);
|
||||
Toy_freeASTNode(node->fnDecl.returns);
|
||||
Toy_freeASTNode(node->fnDecl.block);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_CALL:
|
||||
Toy_freeASTNode(node->fnCall.arguments);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_RETURN:
|
||||
Toy_freeASTNode(node->returns.returns);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_IF:
|
||||
Toy_freeASTNode(node->pathIf.condition);
|
||||
Toy_freeASTNode(node->pathIf.thenPath);
|
||||
Toy_freeASTNode(node->pathIf.elsePath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_WHILE:
|
||||
Toy_freeASTNode(node->pathWhile.condition);
|
||||
Toy_freeASTNode(node->pathWhile.thenPath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FOR:
|
||||
Toy_freeASTNode(node->pathFor.preClause);
|
||||
Toy_freeASTNode(node->pathFor.postClause);
|
||||
Toy_freeASTNode(node->pathFor.condition);
|
||||
Toy_freeASTNode(node->pathFor.thenPath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BREAK:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_CONTINUE:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PREFIX_INCREMENT:
|
||||
Toy_freeLiteral(node->prefixIncrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_PREFIX_DECREMENT:
|
||||
Toy_freeLiteral(node->prefixDecrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_POSTFIX_INCREMENT:
|
||||
Toy_freeLiteral(node->postfixIncrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_POSTFIX_DECREMENT:
|
||||
Toy_freeLiteral(node->postfixDecrement.identifier);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_IMPORT:
|
||||
Toy_freeLiteral(node->import.identifier);
|
||||
Toy_freeLiteral(node->import.alias);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PASS:
|
||||
//EMPTY
|
||||
break;
|
||||
}
|
||||
|
||||
if (freeSelf) {
|
||||
TOY_FREE(Toy_ASTNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_freeASTNode(Toy_ASTNode* node) {
|
||||
freeASTNodeCustom(node, true);
|
||||
}
|
||||
|
||||
//various emitters
|
||||
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal) {
|
||||
//allocate a new node
|
||||
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = TOY_AST_NODE_LITERAL;
|
||||
(*nodeHandle)->atomic.literal = Toy_copyLiteral(literal);
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child) {
|
||||
//allocate a new node
|
||||
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = TOY_AST_NODE_UNARY;
|
||||
(*nodeHandle)->unary.opcode = opcode;
|
||||
(*nodeHandle)->unary.child = child;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BINARY;
|
||||
tmp->binary.opcode = opcode;
|
||||
tmp->binary.left = *nodeHandle;
|
||||
tmp->binary.right = rhs;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_TERNARY;
|
||||
tmp->ternary.condition = condition;
|
||||
tmp->ternary.thenPath = thenPath;
|
||||
tmp->ternary.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_GROUPING;
|
||||
tmp->grouping.child = *nodeHandle;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BLOCK;
|
||||
tmp->block.nodes = NULL; //NOTE: appended by the parser
|
||||
tmp->block.capacity = 0;
|
||||
tmp->block.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_COMPOUND;
|
||||
tmp->compound.literalType = literalType;
|
||||
tmp->compound.nodes = NULL;
|
||||
tmp->compound.capacity = 0;
|
||||
tmp->compound.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right) {
|
||||
//set - assume the node has already been allocated
|
||||
node->type = TOY_AST_NODE_PAIR;
|
||||
node->pair.left = left;
|
||||
node->pair.right = right;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_INDEX;
|
||||
tmp->index.first = first;
|
||||
tmp->index.second = second;
|
||||
tmp->index.third = third;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal typeLiteral, Toy_ASTNode* expression) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_VAR_DECL;
|
||||
tmp->varDecl.identifier = identifier;
|
||||
tmp->varDecl.typeLiteral = typeLiteral;
|
||||
tmp->varDecl.expression = expression;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_COLLECTION;
|
||||
tmp->fnCollection.nodes = NULL;
|
||||
tmp->fnCollection.capacity = 0;
|
||||
tmp->fnCollection.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_DECL;
|
||||
tmp->fnDecl.identifier = identifier;
|
||||
tmp->fnDecl.arguments = arguments;
|
||||
tmp->fnDecl.returns = returns;
|
||||
tmp->fnDecl.block = block;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_CALL;
|
||||
tmp->fnCall.arguments = arguments;
|
||||
tmp->fnCall.argumentCount = arguments->fnCollection.count;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_RETURN;
|
||||
tmp->returns.returns = returns;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_IF;
|
||||
tmp->pathIf.condition = condition;
|
||||
tmp->pathIf.thenPath = thenPath;
|
||||
tmp->pathIf.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_WHILE;
|
||||
tmp->pathWhile.condition = condition;
|
||||
tmp->pathWhile.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FOR;
|
||||
tmp->pathFor.preClause = preClause;
|
||||
tmp->pathFor.condition = condition;
|
||||
tmp->pathFor.postClause = postClause;
|
||||
tmp->pathFor.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BREAK;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_CONTINUE;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PREFIX_INCREMENT;
|
||||
tmp->prefixIncrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PREFIX_DECREMENT;
|
||||
tmp->prefixDecrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_POSTFIX_INCREMENT;
|
||||
tmp->postfixIncrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_POSTFIX_DECREMENT;
|
||||
tmp->postfixDecrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_IMPORT;
|
||||
tmp->import.identifier = Toy_copyLiteral(identifier);
|
||||
tmp->import.alias = Toy_copyLiteral(alias);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePass(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PASS;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_literal.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_token_types.h"
|
||||
|
||||
//nodes are the intermediaries between parsers and compilers
|
||||
typedef union Toy_private_node Toy_ASTNode;
|
||||
|
||||
typedef enum Toy_ASTNodeType {
|
||||
TOY_AST_NODE_ERROR,
|
||||
TOY_AST_NODE_LITERAL, //a simple value
|
||||
TOY_AST_NODE_UNARY, //one child + opcode
|
||||
TOY_AST_NODE_BINARY, //two children, left and right + opcode
|
||||
TOY_AST_NODE_TERNARY, //three children, condition, then path & else path
|
||||
TOY_AST_NODE_GROUPING, //one child
|
||||
TOY_AST_NODE_BLOCK, //contains a sub-node array
|
||||
TOY_AST_NODE_COMPOUND, //contains a sub-node array
|
||||
TOY_AST_NODE_PAIR, //contains a left and right
|
||||
TOY_AST_NODE_INDEX, //index a variable
|
||||
TOY_AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
|
||||
TOY_AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
|
||||
TOY_AST_NODE_FN_COLLECTION, //parts of a function
|
||||
TOY_AST_NODE_FN_CALL, //call a function
|
||||
TOY_AST_NODE_FN_RETURN, //for control flow
|
||||
TOY_AST_NODE_IF, //for control flow
|
||||
TOY_AST_NODE_WHILE, //for control flow
|
||||
TOY_AST_NODE_FOR, //for control flow
|
||||
TOY_AST_NODE_BREAK, //for control flow
|
||||
TOY_AST_NODE_CONTINUE, //for control flow
|
||||
TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable
|
||||
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||
TOY_AST_NODE_POSTFIX_DECREMENT, //decrement a variable
|
||||
TOY_AST_NODE_IMPORT, //import a library
|
||||
TOY_AST_NODE_PASS, //for doing nothing
|
||||
} Toy_ASTNodeType;
|
||||
|
||||
//literals
|
||||
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal);
|
||||
|
||||
typedef struct Toy_NodeLiteral {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal literal;
|
||||
} Toy_NodeLiteral;
|
||||
|
||||
//unary operator
|
||||
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child);
|
||||
|
||||
typedef struct Toy_NodeUnary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Opcode opcode;
|
||||
Toy_ASTNode* child;
|
||||
} Toy_NodeUnary;
|
||||
|
||||
//binary operator
|
||||
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode); //handled node becomes lhs
|
||||
|
||||
typedef struct Toy_NodeBinary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Opcode opcode;
|
||||
Toy_ASTNode* left;
|
||||
Toy_ASTNode* right;
|
||||
} Toy_NodeBinary;
|
||||
|
||||
//ternary operator
|
||||
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
|
||||
|
||||
typedef struct Toy_NodeTernary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
Toy_ASTNode* elsePath;
|
||||
} Toy_NodeTernary;
|
||||
|
||||
//grouping of other AST nodes
|
||||
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeGrouping {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* child;
|
||||
} Toy_NodeGrouping;
|
||||
|
||||
//block of statement nodes
|
||||
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeBlock {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeBlock;
|
||||
|
||||
//compound literals (array, dictionary)
|
||||
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType);
|
||||
|
||||
typedef struct Toy_NodeCompound {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_LiteralType literalType;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeCompound;
|
||||
|
||||
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right); //NOTE: this is a set function, not an emit function
|
||||
|
||||
typedef struct Toy_NodePair {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* left;
|
||||
Toy_ASTNode* right;
|
||||
} Toy_NodePair;
|
||||
|
||||
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third);
|
||||
|
||||
typedef struct Toy_NodeIndex {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* first;
|
||||
Toy_ASTNode* second;
|
||||
Toy_ASTNode* third;
|
||||
} Toy_NodeIndex;
|
||||
|
||||
//variable declaration
|
||||
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal type, Toy_ASTNode* expression);
|
||||
|
||||
typedef struct Toy_NodeVarDecl {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_Literal typeLiteral;
|
||||
Toy_ASTNode* expression;
|
||||
} Toy_NodeVarDecl;
|
||||
|
||||
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
|
||||
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeFnCollection {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeFnCollection;
|
||||
|
||||
//function declaration
|
||||
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block);
|
||||
|
||||
typedef struct Toy_NodeFnDecl {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_ASTNode* arguments;
|
||||
Toy_ASTNode* returns;
|
||||
Toy_ASTNode* block;
|
||||
} Toy_NodeFnDecl;
|
||||
|
||||
//function call
|
||||
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments);
|
||||
|
||||
typedef struct Toy_NodeFnCall {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* arguments;
|
||||
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
|
||||
} Toy_NodeFnCall;
|
||||
|
||||
//function return
|
||||
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns);
|
||||
|
||||
typedef struct Toy_NodeFnReturn {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* returns;
|
||||
} Toy_NodeFnReturn;
|
||||
|
||||
//control flow path - if-else, while, for, break, continue, return
|
||||
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
|
||||
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath);
|
||||
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath);
|
||||
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle);
|
||||
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeIf {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
Toy_ASTNode* elsePath;
|
||||
} Toy_NodeIf;
|
||||
|
||||
typedef struct Toy_NodeWhile {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
} Toy_NodeWhile;
|
||||
|
||||
typedef struct Toy_NodeFor {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* preClause;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* postClause;
|
||||
Toy_ASTNode* thenPath;
|
||||
} Toy_NodeFor;
|
||||
|
||||
typedef struct Toy_NodeBreak {
|
||||
Toy_ASTNodeType type;
|
||||
} Toy_NodeBreak;
|
||||
|
||||
typedef struct Toy_NodeContinue {
|
||||
Toy_ASTNodeType type;
|
||||
} Toy_NodeContinue;
|
||||
|
||||
//pre-post increment/decrement
|
||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
|
||||
typedef struct Toy_NodePrefixIncrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePrefixIncrement;
|
||||
|
||||
typedef struct Toy_NodePrefixDecrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePrefixDecrement;
|
||||
|
||||
typedef struct Toy_NodePostfixIncrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePostfixIncrement;
|
||||
|
||||
typedef struct Toy_NodePostfixDecrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePostfixDecrement;
|
||||
|
||||
//import a library
|
||||
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
typedef struct Toy_NodeImport {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_Literal alias;
|
||||
} Toy_NodeImport;
|
||||
|
||||
//for doing nothing
|
||||
void Toy_emitASTNodePass(Toy_ASTNode** nodeHandle);
|
||||
|
||||
union Toy_private_node {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_NodeLiteral atomic;
|
||||
Toy_NodeUnary unary;
|
||||
Toy_NodeBinary binary;
|
||||
Toy_NodeTernary ternary;
|
||||
Toy_NodeGrouping grouping;
|
||||
Toy_NodeBlock block;
|
||||
Toy_NodeCompound compound;
|
||||
Toy_NodePair pair;
|
||||
Toy_NodeIndex index;
|
||||
Toy_NodeVarDecl varDecl;
|
||||
Toy_NodeFnCollection fnCollection;
|
||||
Toy_NodeFnDecl fnDecl;
|
||||
Toy_NodeFnCall fnCall;
|
||||
Toy_NodeFnReturn returns;
|
||||
Toy_NodeIf pathIf;
|
||||
Toy_NodeWhile pathWhile;
|
||||
Toy_NodeFor pathFor;
|
||||
Toy_NodeBreak pathBreak;
|
||||
Toy_NodeContinue pathContinue;
|
||||
Toy_NodePrefixIncrement prefixIncrement;
|
||||
Toy_NodePrefixDecrement prefixDecrement;
|
||||
Toy_NodePostfixIncrement postfixIncrement;
|
||||
Toy_NodePostfixDecrement postfixDecrement;
|
||||
Toy_NodeImport import;
|
||||
};
|
||||
|
||||
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
|
||||
@@ -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);
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//test variable sizes based on platform - see issue #35
|
||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||
|
||||
STATIC_ASSERT(sizeof(char) == 1);
|
||||
STATIC_ASSERT(sizeof(short) == 2);
|
||||
STATIC_ASSERT(sizeof(int) == 4);
|
||||
STATIC_ASSERT(sizeof(float) == 4);
|
||||
STATIC_ASSERT(sizeof(unsigned char) == 1);
|
||||
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
||||
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
||||
|
||||
static const char* build = __DATE__ " " __TIME__;
|
||||
|
||||
const char* Toy_private_version_build() {
|
||||
return build;
|
||||
}
|
||||
|
||||
//declare the singleton with default values
|
||||
Toy_CommandLine Toy_commandLine = {
|
||||
.error = false,
|
||||
.help = false,
|
||||
.version = false,
|
||||
.binaryfile = NULL,
|
||||
.sourcefile = NULL,
|
||||
.compilefile = NULL,
|
||||
.outfile = "out.tb",
|
||||
.source = NULL,
|
||||
.initialfile = NULL,
|
||||
.enablePrintNewline = true,
|
||||
.parseBytecodeHeader = false,
|
||||
.verbose = false
|
||||
};
|
||||
|
||||
void Toy_initCommandLine(int argc, const char* argv[]) {
|
||||
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||
Toy_commandLine.error = true; //error state by default, set to false by successful flags
|
||||
|
||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
Toy_commandLine.help = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||
Toy_commandLine.version = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
||||
Toy_commandLine.verbose = true;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
||||
Toy_commandLine.sourcefile = (char*)argv[i + 1];
|
||||
i++;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
||||
Toy_commandLine.source = (char*)argv[i + 1];
|
||||
i++;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
||||
Toy_commandLine.compilefile = (char*)argv[i + 1];
|
||||
i++;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
||||
Toy_commandLine.outfile = (char*)argv[i + 1];
|
||||
i++;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-t") || !strcmp(argv[i], "--initial")) && i + 1 < argc) {
|
||||
Toy_commandLine.initialfile = (char*)argv[i + 1];
|
||||
i++;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-p")) {
|
||||
Toy_commandLine.parseBytecodeHeader = true;
|
||||
|
||||
if (Toy_commandLine.binaryfile) {
|
||||
Toy_commandLine.error = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-n")) {
|
||||
Toy_commandLine.enablePrintNewline = false;
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
//option without a flag + ending in .tb = binary input
|
||||
if (i < argc) {
|
||||
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
||||
Toy_commandLine.binaryfile = (char*)argv[i];
|
||||
Toy_commandLine.error = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//don't keep reading in an error state
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 Toy_helpCommandLine(int argc, const char* argv[]) {
|
||||
Toy_usageCommandLine(argc, argv);
|
||||
|
||||
printf(" -h, --help\t\t\tShow this help then exit.\n");
|
||||
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
|
||||
printf(" -d, --debug\t\t\tBe verbose when operating.\n");
|
||||
printf(" -f, --file filename\t\tParse, compile and execute the source file.\n");
|
||||
printf(" -i, --input source\t\tParse, compile and execute this given string of source code.\n");
|
||||
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
|
||||
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
|
||||
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
|
||||
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
|
||||
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
|
||||
}
|
||||
|
||||
void 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-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");
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TOY_VERSION_MAJOR 1
|
||||
#define TOY_VERSION_MINOR 1
|
||||
#define TOY_VERSION_PATCH 5
|
||||
#define TOY_VERSION_BUILD Toy_private_version_build()
|
||||
|
||||
//platform/compiler-specific instructions
|
||||
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||
|
||||
#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 extern
|
||||
|
||||
#endif
|
||||
|
||||
TOY_API const char* Toy_private_version_build();
|
||||
|
||||
//for processing the command line arguments in the repl
|
||||
typedef struct {
|
||||
bool error;
|
||||
bool help;
|
||||
bool version;
|
||||
char* binaryfile;
|
||||
char* sourcefile;
|
||||
char* compilefile;
|
||||
char* outfile; //defaults to out.tb
|
||||
char* source;
|
||||
char* initialfile;
|
||||
bool enablePrintNewline;
|
||||
bool parseBytecodeHeader;
|
||||
bool verbose;
|
||||
} Toy_CommandLine;
|
||||
|
||||
//these are intended for the repl only, despite using the api prefix
|
||||
TOY_API Toy_CommandLine Toy_commandLine;
|
||||
|
||||
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
|
||||
|
||||
TOY_API void Toy_usageCommandLine(int argc, const char* argv[]);
|
||||
TOY_API void Toy_helpCommandLine(int argc, const char* argv[]);
|
||||
TOY_API void Toy_copyrightCommandLine(int argc, const char* argv[]);
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_ast_node.h"
|
||||
#include "toy_literal_array.h"
|
||||
|
||||
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
||||
typedef struct Toy_Compiler {
|
||||
Toy_LiteralArray literalCache;
|
||||
unsigned char* bytecode;
|
||||
int capacity;
|
||||
int count;
|
||||
bool panic;
|
||||
} Toy_Compiler;
|
||||
|
||||
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
|
||||
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
|
||||
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
|
||||
|
||||
//embed the header, data section, code section, function section, etc.
|
||||
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size);
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
/* toy_console_colors.h - console utility
|
||||
|
||||
This file provides a number of macros that can set the color of text in a console
|
||||
window. These are used for convenience only. They are supposed to be dropped into
|
||||
a printf()'s first argument, like so:
|
||||
|
||||
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
|
||||
|
||||
NOTE: you need both font AND background for these to work
|
||||
|
||||
*/
|
||||
|
||||
//platform/compiler-specific instructions
|
||||
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||
|
||||
//fonts color
|
||||
#define TOY_CC_FONT_BLACK "\033[30;"
|
||||
#define TOY_CC_FONT_RED "\033[31;"
|
||||
#define TOY_CC_FONT_GREEN "\033[32;"
|
||||
#define TOY_CC_FONT_YELLOW "\033[33;"
|
||||
#define TOY_CC_FONT_BLUE "\033[34;"
|
||||
#define TOY_CC_FONT_PURPLE "\033[35;"
|
||||
#define TOY_CC_FONT_DGREEN "\033[6;"
|
||||
#define TOY_CC_FONT_WHITE "\033[7;"
|
||||
#define TOY_CC_FONT_CYAN "\x1b[36m"
|
||||
|
||||
//background color
|
||||
#define TOY_CC_BACK_BLACK "40m"
|
||||
#define TOY_CC_BACK_RED "41m"
|
||||
#define TOY_CC_BACK_GREEN "42m"
|
||||
#define TOY_CC_BACK_YELLOW "43m"
|
||||
#define TOY_CC_BACK_BLUE "44m"
|
||||
#define TOY_CC_BACK_PURPLE "45m"
|
||||
#define TOY_CC_BACK_DGREEN "46m"
|
||||
#define TOY_CC_BACK_WHITE "47m"
|
||||
|
||||
//useful
|
||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_RESET "\033[0m"
|
||||
|
||||
#else
|
||||
|
||||
//fonts color
|
||||
#define TOY_CC_FONT_BLACK
|
||||
#define TOY_CC_FONT_RED
|
||||
#define TOY_CC_FONT_GREEN
|
||||
#define TOY_CC_FONT_YELLOW
|
||||
#define TOY_CC_FONT_BLUE
|
||||
#define TOY_CC_FONT_PURPLE
|
||||
#define TOY_CC_FONT_DGREEN
|
||||
#define TOY_CC_FONT_WHITE
|
||||
#define TOY_CC_FONT_CYAN
|
||||
|
||||
//background color
|
||||
#define TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_BACK_RED
|
||||
#define TOY_CC_BACK_GREEN
|
||||
#define TOY_CC_BACK_YELLOW
|
||||
#define TOY_CC_BACK_BLUE
|
||||
#define TOY_CC_BACK_PURPLE
|
||||
#define TOY_CC_BACK_DGREEN
|
||||
#define TOY_CC_BACK_WHITE
|
||||
|
||||
//useful
|
||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_RESET
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "toy_drive_system.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//file system API
|
||||
static Toy_LiteralDictionary driveDictionary;
|
||||
|
||||
void Toy_initDriveSystem() {
|
||||
Toy_initLiteralDictionary(&driveDictionary);
|
||||
}
|
||||
|
||||
void Toy_freeDriveSystem() {
|
||||
Toy_freeLiteralDictionary(&driveDictionary);
|
||||
}
|
||||
|
||||
TOY_API void Toy_setDrivePath(char* drive, char* path) {
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
|
||||
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
|
||||
|
||||
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
|
||||
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
|
||||
//check argument types
|
||||
if (!TOY_IS_STRING(*drivePathLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
size_t driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
driveLength++;
|
||||
}
|
||||
|
||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||
|
||||
//get the real drive file path
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
|
||||
|
||||
if (!TOY_IS_STRING(pathLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\n");
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
Toy_deleteRefString(filePath);
|
||||
Toy_deleteRefString(drivePath);
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
|
||||
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
|
||||
|
||||
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
|
||||
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
|
||||
|
||||
//clean up the drive/path stuff
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_deleteRefString(filePath);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
|
||||
//check for break-out attempts
|
||||
for (size_t i = 0; i < fileLength - 1; i++) {
|
||||
if (file[i] == '.' && file[i + 1] == '.') {
|
||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
|
||||
|
||||
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
//file system API - these need to be set by the host
|
||||
TOY_API void Toy_initDriveSystem();
|
||||
TOY_API void Toy_freeDriveSystem();
|
||||
|
||||
//file system API - for use with libs
|
||||
TOY_API void Toy_setDrivePath(char* drive, char* path);
|
||||
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
//the interpreter acts depending on the bytecode instructions
|
||||
typedef struct Toy_Interpreter {
|
||||
//input
|
||||
const unsigned char* bytecode;
|
||||
int length;
|
||||
int count;
|
||||
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
||||
Toy_LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||
|
||||
//operation
|
||||
Toy_Scope* scope;
|
||||
Toy_LiteralArray stack;
|
||||
|
||||
//Library APIs
|
||||
Toy_LiteralDictionary* hooks;
|
||||
|
||||
//debug outputs
|
||||
Toy_PrintFn printOutput;
|
||||
Toy_PrintFn assertOutput;
|
||||
Toy_PrintFn errorOutput;
|
||||
|
||||
int depth; //don't overflow
|
||||
bool panic;
|
||||
} Toy_Interpreter;
|
||||
|
||||
//native API
|
||||
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func);
|
||||
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook);
|
||||
|
||||
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
|
||||
//utilities for the host program
|
||||
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
|
||||
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
|
||||
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
|
||||
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
|
||||
|
||||
//main access
|
||||
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
|
||||
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length); //run the code
|
||||
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "toy_keyword_types.h"
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
Toy_KeywordType Toy_keywordTypes[] = {
|
||||
//type keywords
|
||||
{TOY_TOKEN_NULL, "null"},
|
||||
{TOY_TOKEN_BOOLEAN, "bool"},
|
||||
{TOY_TOKEN_INTEGER, "int"},
|
||||
{TOY_TOKEN_FLOAT, "float"},
|
||||
{TOY_TOKEN_STRING, "string"},
|
||||
{TOY_TOKEN_FUNCTION, "fn"},
|
||||
{TOY_TOKEN_OPAQUE, "opaque"},
|
||||
{TOY_TOKEN_ANY, "any"},
|
||||
|
||||
//other keywords
|
||||
{TOY_TOKEN_AS, "as"},
|
||||
{TOY_TOKEN_ASSERT, "assert"},
|
||||
{TOY_TOKEN_BREAK, "break"},
|
||||
{TOY_TOKEN_CLASS, "class"},
|
||||
{TOY_TOKEN_CONST, "const"},
|
||||
{TOY_TOKEN_CONTINUE, "continue"},
|
||||
{TOY_TOKEN_DO, "do"},
|
||||
{TOY_TOKEN_ELSE, "else"},
|
||||
{TOY_TOKEN_EXPORT, "export"},
|
||||
{TOY_TOKEN_FOR, "for"},
|
||||
{TOY_TOKEN_FOREACH, "foreach"},
|
||||
{TOY_TOKEN_IF, "if"},
|
||||
{TOY_TOKEN_IMPORT, "import"},
|
||||
{TOY_TOKEN_IN, "in"},
|
||||
{TOY_TOKEN_OF, "of"},
|
||||
{TOY_TOKEN_PRINT, "print"},
|
||||
{TOY_TOKEN_RETURN, "return"},
|
||||
{TOY_TOKEN_TYPE, "type"},
|
||||
{TOY_TOKEN_ASTYPE, "astype"},
|
||||
{TOY_TOKEN_TYPEOF, "typeof"},
|
||||
{TOY_TOKEN_VAR, "var"},
|
||||
{TOY_TOKEN_WHILE, "while"},
|
||||
|
||||
//literal values
|
||||
{TOY_TOKEN_LITERAL_TRUE, "true"},
|
||||
{TOY_TOKEN_LITERAL_FALSE, "false"},
|
||||
|
||||
//meta tokens
|
||||
{TOY_TOKEN_PASS, NULL},
|
||||
{TOY_TOKEN_ERROR, NULL},
|
||||
|
||||
{TOY_TOKEN_EOF, NULL},
|
||||
};
|
||||
|
||||
char* Toy_findKeywordByType(Toy_TokenType type) {
|
||||
if (type == TOY_TOKEN_EOF) {
|
||||
return "EOF";
|
||||
}
|
||||
|
||||
for(int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (Toy_keywordTypes[i].type == type) {
|
||||
return Toy_keywordTypes[i].keyword;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_TokenType Toy_findTypeByKeyword(const char* keyword) {
|
||||
const int length = strlen(keyword);
|
||||
|
||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (!strncmp(keyword, Toy_keywordTypes[i].keyword, length)) {
|
||||
return Toy_keywordTypes[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
return TOY_TOKEN_EOF;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_token_types.h"
|
||||
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
char* keyword;
|
||||
} Toy_KeywordType;
|
||||
|
||||
extern Toy_KeywordType Toy_keywordTypes[];
|
||||
|
||||
char* Toy_findKeywordByType(Toy_TokenType type);
|
||||
|
||||
Toy_TokenType Toy_findTypeByKeyword(const char* keyword);
|
||||
@@ -0,0 +1,383 @@
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_console_colors.h"
|
||||
#include "toy_keyword_types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//static generic utility functions
|
||||
static void cleanLexer(Toy_Lexer* lexer) {
|
||||
lexer->source = NULL;
|
||||
lexer->start = 0;
|
||||
lexer->current = 0;
|
||||
lexer->line = 1;
|
||||
lexer->commentsEnabled = true;
|
||||
}
|
||||
|
||||
static bool isAtEnd(Toy_Lexer* lexer) {
|
||||
return lexer->source[lexer->current] == '\0';
|
||||
}
|
||||
|
||||
static char peek(Toy_Lexer* lexer) {
|
||||
return lexer->source[lexer->current];
|
||||
}
|
||||
|
||||
static char peekNext(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) return '\0';
|
||||
return lexer->source[lexer->current + 1];
|
||||
}
|
||||
|
||||
static char advance(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
//new line
|
||||
if (lexer->source[lexer->current] == '\n') {
|
||||
lexer->line++;
|
||||
}
|
||||
|
||||
lexer->current++;
|
||||
return lexer->source[lexer->current - 1];
|
||||
}
|
||||
|
||||
static void eatWhitespace(Toy_Lexer* lexer) {
|
||||
const char c = peek(lexer);
|
||||
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
advance(lexer);
|
||||
break;
|
||||
|
||||
//comments
|
||||
case '/':
|
||||
if (!lexer->commentsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
//eat the line
|
||||
if (peekNext(lexer) == '/') {
|
||||
while (!isAtEnd(lexer) && advance(lexer) != '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
//eat the block
|
||||
if (peekNext(lexer) == '*') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
while(!isAtEnd(lexer) && !(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
//tail recursion
|
||||
eatWhitespace(lexer);
|
||||
}
|
||||
|
||||
static bool isDigit(Toy_Lexer* lexer) {
|
||||
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(Toy_Lexer* lexer) {
|
||||
return
|
||||
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
||||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
||||
peek(lexer) == '_'
|
||||
;
|
||||
}
|
||||
|
||||
static bool match(Toy_Lexer* lexer, char c) {
|
||||
if (peek(lexer) == c) {
|
||||
advance(lexer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//token generators
|
||||
static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_ERROR;
|
||||
token.lexeme = msg;
|
||||
token.length = strlen(msg);
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("err:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = type;
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.lexeme = &lexer->source[lexer->current - token.length];
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("tok:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
|
||||
|
||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||
|
||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
||||
type = TOY_TOKEN_LITERAL_FLOAT;
|
||||
advance(lexer);
|
||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
|
||||
token.type = type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
if (type == TOY_TOKEN_LITERAL_INTEGER) {
|
||||
printf("int:");
|
||||
} else {
|
||||
printf("flt:");
|
||||
}
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static bool isEscapableCharacter(char c) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
case 't':
|
||||
case '\\':
|
||||
case '"':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||
while (!isAtEnd(lexer)) {
|
||||
//stop if you've hit the terminator
|
||||
if (peek(lexer) == terminator) {
|
||||
advance(lexer); //eat terminator
|
||||
break;
|
||||
}
|
||||
|
||||
//skip escaped control characters
|
||||
if (peek(lexer) == '\\' && isEscapableCharacter(peekNext(lexer))) {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
continue;
|
||||
}
|
||||
|
||||
//otherwise
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
if (isAtEnd(lexer)) {
|
||||
return makeErrorToken(lexer, "Unterminated string");
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_LITERAL_STRING;
|
||||
token.lexeme = &lexer->source[lexer->start + 1];
|
||||
token.length = lexer->current - lexer->start - 2;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("str:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
advance(lexer); //first letter can only be alpha
|
||||
|
||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
//scan for a keyword
|
||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = Toy_keywordTypes[i].type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("kwd:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
//return an identifier
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_IDENTIFIER;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (Toy_commandLine.verbose) {
|
||||
printf("idf:");
|
||||
Toy_private_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
|
||||
cleanLexer(lexer);
|
||||
|
||||
lexer->source = source;
|
||||
}
|
||||
|
||||
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
||||
eatWhitespace(lexer);
|
||||
|
||||
lexer->start = lexer->current;
|
||||
|
||||
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
|
||||
|
||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||
|
||||
char c = advance(lexer);
|
||||
|
||||
switch(c) {
|
||||
case '(': return makeToken(lexer, TOY_TOKEN_PAREN_LEFT);
|
||||
case ')': return makeToken(lexer, TOY_TOKEN_PAREN_RIGHT);
|
||||
case '{': return makeToken(lexer, TOY_TOKEN_BRACE_LEFT);
|
||||
case '}': return makeToken(lexer, TOY_TOKEN_BRACE_RIGHT);
|
||||
case '[': return makeToken(lexer, TOY_TOKEN_BRACKET_LEFT);
|
||||
case ']': return makeToken(lexer, TOY_TOKEN_BRACKET_RIGHT);
|
||||
|
||||
case '+': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOY_TOKEN_PLUS_PLUS: TOY_TOKEN_PLUS);
|
||||
case '-': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOY_TOKEN_MINUS_MINUS: TOY_TOKEN_MINUS);
|
||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MULTIPLY_ASSIGN : TOY_TOKEN_MULTIPLY);
|
||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_DIVIDE_ASSIGN : TOY_TOKEN_DIVIDE);
|
||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MODULO_ASSIGN : TOY_TOKEN_MODULO);
|
||||
|
||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_NOT_EQUAL : TOY_TOKEN_NOT);
|
||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_EQUAL : TOY_TOKEN_ASSIGN);
|
||||
|
||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_LESS_EQUAL : TOY_TOKEN_LESS);
|
||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_GREATER_EQUAL : TOY_TOKEN_GREATER);
|
||||
|
||||
case '&': //TOKEN_AND not used
|
||||
if (advance(lexer) != '&') {
|
||||
return makeErrorToken(lexer, "Unexpected '&'");
|
||||
} else {
|
||||
return makeToken(lexer, TOY_TOKEN_AND);
|
||||
}
|
||||
|
||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
|
||||
|
||||
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
|
||||
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
|
||||
case ';': return makeToken(lexer, TOY_TOKEN_SEMICOLON);
|
||||
case ',': return makeToken(lexer, TOY_TOKEN_COMMA);
|
||||
case '.':
|
||||
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
return makeToken(lexer, TOY_TOKEN_REST);
|
||||
}
|
||||
return makeToken(lexer, TOY_TOKEN_DOT);
|
||||
|
||||
case '"':
|
||||
return makeString(lexer, c);
|
||||
//TODO: possibly support interpolated strings
|
||||
|
||||
default: {
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "Unexpected token: %c", c);
|
||||
return makeErrorToken(lexer, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void trim(char** s, int* l) { //all this to remove a newline?
|
||||
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
||||
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
||||
}
|
||||
|
||||
//for debugging
|
||||
void Toy_private_printToken(Toy_Token* token) {
|
||||
if (token->type == TOY_TOKEN_ERROR) {
|
||||
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\t%d\t%d\t", token->type, token->line);
|
||||
|
||||
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
|
||||
printf("%.*s\t", token->length, token->lexeme);
|
||||
} else {
|
||||
char* keyword = Toy_findKeywordByType(token->type);
|
||||
|
||||
if (keyword != NULL) {
|
||||
printf("%s", keyword);
|
||||
} else {
|
||||
char* str = (char*)token->lexeme; //strip const-ness for trimming
|
||||
int length = token->length;
|
||||
trim(&str, &length);
|
||||
printf("%.*s", length, str);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
|
||||
lexer->commentsEnabled = enabled;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_token_types.h"
|
||||
|
||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||
typedef struct {
|
||||
const char* source;
|
||||
int start; //start of the token
|
||||
int current; //current position of the lexer
|
||||
int line; //track this for error handling
|
||||
bool commentsEnabled; //BUGFIX: enable comments (disabled in repl)
|
||||
} Toy_Lexer;
|
||||
|
||||
//tokens are intermediaries between lexers and parsers
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
const char* lexeme;
|
||||
int length;
|
||||
int line;
|
||||
} Toy_Token;
|
||||
|
||||
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
|
||||
TOY_API Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
|
||||
|
||||
//for debugging
|
||||
TOY_API void Toy_private_printToken(Toy_Token* token);
|
||||
|
||||
TOY_API void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
|
||||
@@ -0,0 +1,737 @@
|
||||
#include "toy_literal.h"
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//hash util functions
|
||||
static unsigned int hashString(const char* string, int length) {
|
||||
unsigned int hash = 2166136261u;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
hash *= string[i];
|
||||
hash ^= 16777619;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned int hashUInt(unsigned int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_freeLiteral(Toy_Literal literal) {
|
||||
//refstrings
|
||||
if (TOY_IS_STRING(literal)) {
|
||||
Toy_deleteRefString(TOY_AS_STRING(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(literal)) {
|
||||
Toy_deleteRefString(TOY_AS_IDENTIFIER(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//compounds
|
||||
if (TOY_IS_ARRAY(literal) || literal.type == TOY_LITERAL_ARRAY_INTERMEDIATE || literal.type == TOY_LITERAL_DICTIONARY_INTERMEDIATE || literal.type == TOY_LITERAL_TYPE_INTERMEDIATE) {
|
||||
Toy_freeLiteralArray(TOY_AS_ARRAY(literal));
|
||||
TOY_FREE(Toy_LiteralArray, TOY_AS_ARRAY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_DICTIONARY(literal)) {
|
||||
Toy_freeLiteralDictionary(TOY_AS_DICTIONARY(literal));
|
||||
TOY_FREE(Toy_LiteralDictionary, TOY_AS_DICTIONARY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//complex literals
|
||||
if (TOY_IS_FUNCTION(literal)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
|
||||
TOY_AS_FUNCTION(literal).scope = NULL;
|
||||
Toy_deleteRefFunction((Toy_RefFunction*)(TOY_AS_FUNCTION(literal).inner.ptr));
|
||||
}
|
||||
|
||||
if (TOY_IS_TYPE(literal) && TOY_AS_TYPE(literal).capacity > 0) {
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_Literal, TOY_AS_TYPE(literal).subtypes, TOY_AS_TYPE(literal).capacity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_private_isTruthy(Toy_Literal x) {
|
||||
if (TOY_IS_NULL(x)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Null is neither true nor false\n" TOY_CC_RESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_BOOLEAN(x)) {
|
||||
return TOY_AS_BOOLEAN(x);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
|
||||
return ((Toy_Literal){{ .identifier = { .ptr = ptr, .hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }},TOY_LITERAL_IDENTIFIER});
|
||||
}
|
||||
|
||||
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
|
||||
//grow the subtype array
|
||||
if (TOY_AS_TYPE(*lit).count + 1 > TOY_AS_TYPE(*lit).capacity) {
|
||||
int oldCapacity = TOY_AS_TYPE(*lit).capacity;
|
||||
|
||||
TOY_AS_TYPE(*lit).capacity = TOY_GROW_CAPACITY(oldCapacity);
|
||||
TOY_AS_TYPE(*lit).subtypes = TOY_GROW_ARRAY(Toy_Literal, TOY_AS_TYPE(*lit).subtypes, oldCapacity, TOY_AS_TYPE(*lit).capacity);
|
||||
}
|
||||
|
||||
//actually push
|
||||
((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count++ ] = subtype;
|
||||
return &((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count - 1 ];
|
||||
}
|
||||
|
||||
Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
||||
switch(original.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
case TOY_LITERAL_INTEGER:
|
||||
case TOY_LITERAL_FLOAT:
|
||||
//no copying needed
|
||||
return original;
|
||||
|
||||
case TOY_LITERAL_STRING: {
|
||||
return TOY_TO_STRING_LITERAL(Toy_copyRefString(TOY_AS_STRING(original)));
|
||||
}
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//preallocate enough space
|
||||
array->capacity = TOY_AS_ARRAY(original)->capacity;
|
||||
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, 0, array->capacity);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
|
||||
}
|
||||
|
||||
return TOY_TO_ARRAY_LITERAL(array);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//preallocate enough space
|
||||
dictionary->capacity = TOY_AS_DICTIONARY(original)->capacity;
|
||||
dictionary->entries = TOY_ALLOCATE(Toy_private_dictionary_entry, dictionary->capacity);
|
||||
|
||||
for (int i = 0; i < dictionary->capacity; i++) {
|
||||
dictionary->entries[i].key = TOY_TO_NULL_LITERAL;
|
||||
dictionary->entries[i].value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//copy each entry
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
|
||||
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
|
||||
Toy_setLiteralDictionary(dictionary, TOY_AS_DICTIONARY(original)->entries[i].key, TOY_AS_DICTIONARY(original)->entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION: {
|
||||
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(Toy_copyRefFunction( TOY_AS_FUNCTION(original).inner.ptr ));
|
||||
|
||||
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
//NOTE: could optimise this by copying the hash manually, but it's a very small increase in performance
|
||||
return TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_IDENTIFIER(original)));
|
||||
}
|
||||
|
||||
case TOY_LITERAL_TYPE: {
|
||||
Toy_Literal lit = TOY_TO_TYPE_LITERAL(TOY_AS_TYPE(original).typeOf, TOY_AS_TYPE(original).constant);
|
||||
|
||||
for (int i = 0; i < TOY_AS_TYPE(original).count; i++) {
|
||||
TOY_TYPE_PUSH_SUBTYPE(&lit, Toy_copyLiteral( ((Toy_Literal*)(TOY_AS_TYPE(original).subtypes))[i] ));
|
||||
}
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_OPAQUE: {
|
||||
return original; //literally a shallow copy
|
||||
}
|
||||
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_ARRAY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: { //TODO: efficient preallocation?
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_TYPE_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
//no copying possible
|
||||
return original;
|
||||
|
||||
default:
|
||||
fprintf(stderr, TOY_CC_ERROR "Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
|
||||
//utility for other things
|
||||
if (lhs.type != rhs.type) {
|
||||
// ints and floats are compatible
|
||||
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
|
||||
if (TOY_IS_INTEGER(lhs)) {
|
||||
return TOY_AS_INTEGER(lhs) == TOY_AS_FLOAT(rhs);
|
||||
}
|
||||
else {
|
||||
return TOY_AS_FLOAT(lhs) == TOY_AS_INTEGER(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(lhs.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
return true; //can only be true because of the check above
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
return TOY_AS_BOOLEAN(lhs) == TOY_AS_BOOLEAN(rhs);
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
return TOY_AS_INTEGER(lhs) == TOY_AS_INTEGER(rhs);
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
return TOY_AS_FLOAT(lhs) == TOY_AS_FLOAT(rhs);
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
return Toy_equalsRefString(TOY_AS_STRING(lhs), TOY_AS_STRING(rhs));
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
case TOY_LITERAL_ARRAY_INTERMEDIATE:
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
||||
//mismatched sizes
|
||||
if (TOY_AS_ARRAY(lhs)->count != TOY_AS_ARRAY(rhs)->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//mismatched elements (in order)
|
||||
for (int i = 0; i < TOY_AS_ARRAY(lhs)->count; i++) {
|
||||
if (!Toy_literalsAreEqual( TOY_AS_ARRAY(lhs)->literals[i], TOY_AS_ARRAY(rhs)->literals[i] )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY:
|
||||
//relatively slow, especially when nested
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(lhs)->capacity; i++) {
|
||||
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
||||
//check it exists in rhs
|
||||
if (!Toy_existsLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//compare the values
|
||||
Toy_Literal val = Toy_getLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
|
||||
if (!Toy_literalsAreEqual(TOY_AS_DICTIONARY(lhs)->entries[i].value, val)) {
|
||||
Toy_freeLiteral(val);
|
||||
return false;
|
||||
}
|
||||
Toy_freeLiteral(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
return false; //functions are never equal
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
//check shortcuts
|
||||
if (TOY_HASH_I(lhs) != TOY_HASH_I(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Toy_equalsRefString(TOY_AS_IDENTIFIER(lhs), TOY_AS_IDENTIFIER(rhs));
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
//check types
|
||||
if (TOY_AS_TYPE(lhs).typeOf != TOY_AS_TYPE(rhs).typeOf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//const don't match
|
||||
if (TOY_AS_TYPE(lhs).constant != TOY_AS_TYPE(rhs).constant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check subtypes
|
||||
if (TOY_AS_TYPE(lhs).count != TOY_AS_TYPE(rhs).count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check array|dictionary signatures are the same (in order)
|
||||
if (TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_DICTIONARY) {
|
||||
for (int i = 0; i < TOY_AS_TYPE(lhs).count; i++) {
|
||||
if (!Toy_literalsAreEqual(((Toy_Literal*)(TOY_AS_TYPE(lhs).subtypes))[i], ((Toy_Literal*)(TOY_AS_TYPE(rhs).subtypes))[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
return false; //IDK what this is!
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't compare intermediate functions\n" TOY_CC_RESET);
|
||||
return false;
|
||||
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
return false;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in equality: %d\n" TOY_CC_RESET, lhs.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Toy_hashLiteral(Toy_Literal lit) {
|
||||
switch(lit.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
return 0;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
return TOY_AS_BOOLEAN(lit) ? 1 : 0;
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
|
||||
|
||||
case TOY_LITERAL_FLOAT: {
|
||||
unsigned int* ptr = (unsigned int*)(&TOY_AS_FLOAT(lit));
|
||||
return hashUInt(*ptr);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < TOY_AS_ARRAY(lit)->count; i++) {
|
||||
res += Toy_hashLiteral(TOY_AS_ARRAY(lit)->literals[i]);
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(lit)->capacity; i++) {
|
||||
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
||||
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].key);
|
||||
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].value);
|
||||
}
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
return -1; //can't hash these
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
return TOY_HASH_I(lit); //pre-computed
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
return -1; //not much i can really do
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
case TOY_LITERAL_ANY:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//utils
|
||||
static void stdoutWrapper(const char* output) {
|
||||
printf("%s", output);
|
||||
}
|
||||
|
||||
//buffer the prints
|
||||
static char* globalPrintBuffer = NULL;
|
||||
static size_t globalPrintCapacity = 0;
|
||||
static size_t globalPrintCount = 0;
|
||||
|
||||
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
||||
static char quotes = 0; //set to 0 to not show string quotes
|
||||
|
||||
static void printToBuffer(const char* str) {
|
||||
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
|
||||
int oldCapacity = globalPrintCapacity;
|
||||
|
||||
globalPrintCapacity = TOY_GROW_CAPACITY(globalPrintCapacity);
|
||||
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
||||
}
|
||||
|
||||
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str ? str : "\0");
|
||||
globalPrintCount += strlen(str);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_printLiteral(Toy_Literal literal) {
|
||||
Toy_printLiteralCustom(literal, stdoutWrapper);
|
||||
}
|
||||
|
||||
void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn printFn) {
|
||||
switch(literal.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
printFn("null");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
printFn(TOY_AS_BOOLEAN(literal) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_INTEGER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%d", TOY_AS_INTEGER(literal));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FLOAT: {
|
||||
char buffer[256];
|
||||
|
||||
if (TOY_AS_FLOAT(literal) - (int)TOY_AS_FLOAT(literal)) {
|
||||
snprintf(buffer, 256, "%g", TOY_AS_FLOAT(literal));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, 256, "%.1f", TOY_AS_FLOAT(literal));
|
||||
}
|
||||
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_STRING: {
|
||||
char buffer[TOY_MAX_STRING_LENGTH];
|
||||
if (!quotes) {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
|
||||
}
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
Toy_LiteralArray* ptr = TOY_AS_ARRAY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->count; i++) {
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->literals[i], printToBuffer);
|
||||
|
||||
if (i + 1 < ptr->count) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
}
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
Toy_LiteralDictionary* ptr = TOY_AS_DICTIONARY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
int delimCount = 0;
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->capacity; i++) {
|
||||
if (TOY_IS_NULL(ptr->entries[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delimCount++ > 0) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
||||
printToBuffer(":");
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
||||
}
|
||||
|
||||
//empty dicts MUST have a ":" printed
|
||||
if (ptr->count == 0) {
|
||||
printToBuffer(":");
|
||||
}
|
||||
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_FUNCTION_HOOK:
|
||||
printFn("(function)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%.*s", (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE: {
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the type correctly
|
||||
printToBuffer("<");
|
||||
|
||||
switch(TOY_AS_TYPE(literal).typeOf) {
|
||||
case TOY_LITERAL_NULL:
|
||||
printToBuffer("null");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
printToBuffer("bool");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
printToBuffer("int");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
printToBuffer("float");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
printToBuffer("string");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
//print all in the array
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY:
|
||||
printToBuffer("[");
|
||||
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i += 2) {
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
printToBuffer(":");
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
printToBuffer("function");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
printToBuffer("native");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
printToBuffer("identifier");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
printToBuffer("type");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
printToBuffer("opaque");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
printToBuffer("any");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print type: %d\n" TOY_CC_RESET, TOY_AS_TYPE(literal).typeOf);
|
||||
}
|
||||
|
||||
//const (printed last)
|
||||
if (TOY_AS_TYPE(literal).constant) {
|
||||
printToBuffer(" const");
|
||||
}
|
||||
|
||||
printToBuffer(">");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE:
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
|
||||
printFn("Unprintable literal found");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
printFn("(opaque)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
printFn("(any)");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print: %d\n" TOY_CC_RESET, literal.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_refstring.h"
|
||||
#include "toy_reffunction.h"
|
||||
|
||||
//forward delcare stuff
|
||||
struct Toy_Literal;
|
||||
struct Toy_Interpreter;
|
||||
struct Toy_LiteralArray;
|
||||
struct Toy_LiteralDictionary;
|
||||
struct Toy_Scope;
|
||||
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
|
||||
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
|
||||
typedef void (*Toy_PrintFn)(const char*);
|
||||
|
||||
typedef enum {
|
||||
TOY_LITERAL_NULL,
|
||||
TOY_LITERAL_BOOLEAN,
|
||||
TOY_LITERAL_INTEGER,
|
||||
TOY_LITERAL_FLOAT,
|
||||
TOY_LITERAL_STRING,
|
||||
TOY_LITERAL_ARRAY,
|
||||
TOY_LITERAL_DICTIONARY,
|
||||
TOY_LITERAL_FUNCTION,
|
||||
TOY_LITERAL_IDENTIFIER,
|
||||
TOY_LITERAL_TYPE,
|
||||
TOY_LITERAL_OPAQUE,
|
||||
TOY_LITERAL_ANY,
|
||||
|
||||
//these are meta-level types - not for general use
|
||||
TOY_LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||
TOY_LITERAL_ARRAY_INTERMEDIATE, //used to process arrays in the compiler only
|
||||
TOY_LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
||||
TOY_LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||
TOY_LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
||||
TOY_LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
||||
TOY_LITERAL_FUNCTION_HOOK, //for handling hook functions within literals only
|
||||
TOY_LITERAL_INDEX_BLANK, //for blank indexing i.e. arr[:]
|
||||
} Toy_LiteralType;
|
||||
|
||||
typedef struct Toy_Literal {
|
||||
union {
|
||||
bool boolean; //1
|
||||
int integer; //4
|
||||
float number;//4
|
||||
|
||||
struct {
|
||||
Toy_RefString* ptr; //8
|
||||
//string hash?
|
||||
} string; //8
|
||||
|
||||
struct Toy_LiteralArray* array; //8
|
||||
struct Toy_LiteralDictionary* dictionary; //8
|
||||
|
||||
struct {
|
||||
union {
|
||||
Toy_RefFunction* ptr; //8
|
||||
Toy_NativeFn native; //8
|
||||
Toy_HookFn hook; //8
|
||||
} inner; //8
|
||||
struct Toy_Scope* scope; //8
|
||||
} function; //16
|
||||
|
||||
struct { //for variable names
|
||||
Toy_RefString* ptr; //8
|
||||
int hash; //4
|
||||
} identifier; //16
|
||||
|
||||
struct {
|
||||
struct Toy_Literal* subtypes; //8
|
||||
Toy_LiteralType typeOf; //4
|
||||
unsigned char capacity; //1
|
||||
unsigned char count; //1
|
||||
bool constant; //1
|
||||
} type; //16
|
||||
|
||||
struct {
|
||||
void* ptr; //8
|
||||
int tag; //4
|
||||
} opaque; //16
|
||||
|
||||
void* generic; //8
|
||||
} as; //16
|
||||
|
||||
Toy_LiteralType type; //4
|
||||
//4 - unused
|
||||
//shenanigans with byte alignment reduces the size of Toy_Literal
|
||||
} Toy_Literal;
|
||||
|
||||
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
|
||||
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
||||
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
||||
#define TOY_IS_FLOAT(value) ((value).type == TOY_LITERAL_FLOAT)
|
||||
#define TOY_IS_STRING(value) ((value).type == TOY_LITERAL_STRING)
|
||||
#define TOY_IS_ARRAY(value) ((value).type == TOY_LITERAL_ARRAY)
|
||||
#define TOY_IS_DICTIONARY(value) ((value).type == TOY_LITERAL_DICTIONARY)
|
||||
#define TOY_IS_FUNCTION(value) ((value).type == TOY_LITERAL_FUNCTION)
|
||||
#define TOY_IS_FUNCTION_NATIVE(value) ((value).type == TOY_LITERAL_FUNCTION_NATIVE)
|
||||
#define TOY_IS_FUNCTION_HOOK(value) ((value).type == TOY_LITERAL_FUNCTION_HOOK)
|
||||
#define TOY_IS_IDENTIFIER(value) ((value).type == TOY_LITERAL_IDENTIFIER)
|
||||
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
|
||||
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
|
||||
|
||||
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
|
||||
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
||||
#define TOY_AS_FLOAT(value) ((value).as.number)
|
||||
#define TOY_AS_STRING(value) ((value).as.string.ptr)
|
||||
#define TOY_AS_ARRAY(value) ((Toy_LiteralArray*)((value).as.array))
|
||||
#define TOY_AS_DICTIONARY(value) ((Toy_LiteralDictionary*)((value).as.dictionary))
|
||||
#define TOY_AS_FUNCTION(value) ((value).as.function)
|
||||
#define TOY_AS_FUNCTION_NATIVE(value) ((value).as.function.inner.native)
|
||||
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.inner.hook)
|
||||
#define TOY_AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||
#define TOY_AS_TYPE(value) ((value).as.type)
|
||||
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||
|
||||
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL})
|
||||
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN})
|
||||
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER})
|
||||
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){{ .number = value }, TOY_LITERAL_FLOAT})
|
||||
#define TOY_TO_STRING_LITERAL(value) ((Toy_Literal){{ .string = { .ptr = value }},TOY_LITERAL_STRING})
|
||||
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){{ .array = value }, TOY_LITERAL_ARRAY})
|
||||
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){{ .dictionary = value }, TOY_LITERAL_DICTIONARY})
|
||||
#define TOY_TO_FUNCTION_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .ptr = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION})
|
||||
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .native = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_NATIVE})
|
||||
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){{ .function = { .inner = { .hook = value }, .scope = NULL }}, TOY_LITERAL_FUNCTION_HOOK})
|
||||
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
||||
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){{ .type = { .typeOf = value, .constant = c, .subtypes = NULL, .capacity = 0, .count = 0 }}, TOY_LITERAL_TYPE})
|
||||
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){{ .opaque = { .ptr = value, .tag = t }}, TOY_LITERAL_OPAQUE})
|
||||
|
||||
//BUGFIX: For blank indexing
|
||||
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
||||
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
|
||||
|
||||
TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
||||
|
||||
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
|
||||
|
||||
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) (Toy_lengthRefFunction((lit).inner.ptr))
|
||||
|
||||
#define TOY_MAX_STRING_LENGTH 4096
|
||||
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
|
||||
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
|
||||
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||
|
||||
//BUGFIX: macros are not functions
|
||||
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
||||
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
|
||||
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
|
||||
|
||||
//utils
|
||||
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
|
||||
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
|
||||
TOY_API int Toy_hashLiteral(Toy_Literal lit);
|
||||
|
||||
//not thread-safe
|
||||
TOY_API void Toy_printLiteral(Toy_Literal literal);
|
||||
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn);
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "toy_literal_array.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLiteralArray(Toy_LiteralArray* array) {
|
||||
array->capacity = 0;
|
||||
array->count = 0;
|
||||
array->literals = NULL;
|
||||
}
|
||||
|
||||
void Toy_freeLiteralArray(Toy_LiteralArray* array) {
|
||||
//clean up memory
|
||||
for(int i = 0; i < array->count; i++) {
|
||||
Toy_freeLiteral(array->literals[i]);
|
||||
}
|
||||
|
||||
if (array->capacity > 0) {
|
||||
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
|
||||
Toy_initLiteralArray(array);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||
if (array->capacity < array->count + 1) {
|
||||
int oldCapacity = array->capacity;
|
||||
|
||||
array->capacity = TOY_GROW_CAPACITY(oldCapacity);
|
||||
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, oldCapacity, array->capacity);
|
||||
}
|
||||
|
||||
array->literals[array->count] = Toy_copyLiteral(literal);
|
||||
return array->count++;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
|
||||
if (array->count <= 0) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the return
|
||||
Toy_Literal ret = array->literals[array->count-1];
|
||||
|
||||
//null the existing data
|
||||
array->literals[array->count-1] = TOY_TO_NULL_LITERAL;
|
||||
|
||||
array->count--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//find a literal in the array that matches the "literal" argument
|
||||
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||
for (int i = 0; i < array->count; i++) {
|
||||
//not the same type
|
||||
if (array->literals[i].type != literal.type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//types match?
|
||||
if (Toy_literalsAreEqual(array->literals[i], literal)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value) {
|
||||
if (!TOY_IS_INTEGER(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = TOY_AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(array->literals[idx]);
|
||||
array->literals[idx] = Toy_copyLiteral(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index) {
|
||||
if (!TOY_IS_INTEGER(index)) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
int idx = TOY_AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
return Toy_copyLiteral(array->literals[idx]);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
|
||||
typedef struct Toy_LiteralArray {
|
||||
Toy_Literal* literals;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_LiteralArray;
|
||||
|
||||
TOY_API void Toy_initLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API void Toy_freeLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal);
|
||||
TOY_API Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value);
|
||||
TOY_API Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index);
|
||||
|
||||
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
|
||||
|
||||
//TODO: add a function to get the capacity & count
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//util functions
|
||||
static void setEntryValues(Toy_private_dictionary_entry* entry, Toy_Literal key, Toy_Literal value) {
|
||||
//much simpler now
|
||||
Toy_freeLiteral(entry->key);
|
||||
entry->key = Toy_copyLiteral(key);
|
||||
|
||||
Toy_freeLiteral(entry->value);
|
||||
entry->value = Toy_copyLiteral(value);
|
||||
}
|
||||
|
||||
static Toy_private_dictionary_entry* getEntryArray(Toy_private_dictionary_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
|
||||
if (!capacity) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//find "key", starting at index
|
||||
int index = hash % capacity;
|
||||
int start = index;
|
||||
|
||||
//increment once, so it can't equal start
|
||||
if (++index >= capacity) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
//literal probing and collision checking
|
||||
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
||||
Toy_private_dictionary_entry* entry = &array[index];
|
||||
|
||||
if (TOY_IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
||||
if (TOY_IS_NULL(entry->value) && !mustExist) {
|
||||
//found a truly empty bucket
|
||||
return entry;
|
||||
}
|
||||
//else it's a tombstone - ignore
|
||||
} else {
|
||||
if (Toy_literalsAreEqual(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (++index >= capacity) {
|
||||
index = 0;
|
||||
}
|
||||
//index = (index + 1) % capacity;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void adjustEntryCapacity(Toy_private_dictionary_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
||||
//new entry space
|
||||
Toy_private_dictionary_entry* newEntries = TOY_ALLOCATE(Toy_private_dictionary_entry, capacity);
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
newEntries[i].key = TOY_TO_NULL_LITERAL;
|
||||
newEntries[i].value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//move the old array into the new one
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
if (TOY_IS_NULL((*dictionaryHandle)[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//place the key and value in the new array (reusing string memory)
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
|
||||
|
||||
entry->key = (*dictionaryHandle)[i].key;
|
||||
entry->value = (*dictionaryHandle)[i].value;
|
||||
}
|
||||
|
||||
//clear the old array
|
||||
if (oldCapacity > 0) {
|
||||
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
|
||||
}
|
||||
|
||||
*dictionaryHandle = newEntries;
|
||||
}
|
||||
|
||||
static bool setEntryArray(Toy_private_dictionary_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
|
||||
//expand array if needed
|
||||
if (contains + 1 > *capacityPtr * TOY_DICTIONARY_MAX_LOAD) {
|
||||
int oldCapacity = *capacityPtr;
|
||||
*capacityPtr = TOY_GROW_CAPACITY(*capacityPtr);
|
||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||
}
|
||||
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
||||
|
||||
//true = contains increase
|
||||
if (TOY_IS_NULL(entry->key)) {
|
||||
setEntryValues(entry, key, value);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
setEntryValues(entry, key, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void freeEntry(Toy_private_dictionary_entry* entry) {
|
||||
Toy_freeLiteral(entry->key);
|
||||
Toy_freeLiteral(entry->value);
|
||||
entry->key = TOY_TO_NULL_LITERAL;
|
||||
entry->value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
|
||||
if (array == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (!TOY_IS_NULL(array[i].key)) {
|
||||
freeEntry(&array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TOY_FREE_ARRAY(Toy_private_dictionary_entry, array, capacity);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
||||
dictionary->entries = NULL;
|
||||
dictionary->capacity = 0;
|
||||
dictionary->contains = 0;
|
||||
dictionary->count = 0;
|
||||
dictionary->capacity = 0;
|
||||
}
|
||||
|
||||
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||
if (dictionary->capacity > 0) {
|
||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||
dictionary->capacity = 0;
|
||||
dictionary->contains = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key) || TOY_IS_FUNCTION_HOOK(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, Toy_hashLiteral(key));
|
||||
|
||||
if (increment) {
|
||||
dictionary->contains++;
|
||||
dictionary->count++;
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key) || TOY_IS_FUNCTION_HOOK(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
return Toy_copyLiteral(entry->value);
|
||||
}
|
||||
else {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key) || TOY_IS_FUNCTION_HOOK(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
freeEntry(entry);
|
||||
entry->value = TOY_TO_BOOLEAN_LITERAL(true); //tombstone
|
||||
dictionary->count--;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
//null & not tombstoned
|
||||
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
|
||||
return entry != NULL && !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
|
||||
//TODO: benchmark this
|
||||
#define TOY_DICTIONARY_MAX_LOAD 0.75
|
||||
|
||||
typedef struct Toy_private_dictionary_entry {
|
||||
Toy_Literal key;
|
||||
Toy_Literal value;
|
||||
} Toy_private_dictionary_entry;
|
||||
|
||||
typedef struct Toy_LiteralDictionary {
|
||||
Toy_private_dictionary_entry* entries;
|
||||
int capacity;
|
||||
int count;
|
||||
int contains; //count + tombstones, for internal use
|
||||
} Toy_LiteralDictionary;
|
||||
|
||||
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||
|
||||
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
|
||||
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
|
||||
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "toy_memory.h"
|
||||
#include "toy_refstring.h"
|
||||
#include "toy_reffunction.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//default allocator
|
||||
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
//causes issues, so just skip out with a NO-OP (DISABLED for performance reasons)
|
||||
// if (newSize == 0 && oldSize == 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
if (newSize == 0) {
|
||||
free(pointer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* mem = realloc(pointer, newSize);
|
||||
|
||||
if (mem == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %zu, replacing %zu)\n" TOY_CC_RESET, newSize, oldSize);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
//static variables
|
||||
static Toy_MemoryAllocatorFn allocator = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
//exposed API
|
||||
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||
return allocator(pointer, oldSize, newSize);
|
||||
}
|
||||
|
||||
void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
|
||||
if (fn == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't be null)\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fn == Toy_reallocate) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't loop the Toy_reallocate function)\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
allocator = fn;
|
||||
Toy_setRefStringAllocatorFn(fn);
|
||||
Toy_setRefFunctionAllocatorFn(fn);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
||||
|
||||
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
|
||||
|
||||
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
|
||||
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||
|
||||
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
|
||||
//implementation details
|
||||
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||
|
||||
//assign the memory allocator
|
||||
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
||||
@@ -0,0 +1,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;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_ast_node.h"
|
||||
|
||||
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
|
||||
typedef struct {
|
||||
Toy_Lexer* lexer;
|
||||
bool error; //I've had an error
|
||||
bool panic; //I am processing an error
|
||||
|
||||
//track the last two outputs from the lexer
|
||||
Toy_Token current;
|
||||
Toy_Token previous;
|
||||
} Toy_Parser;
|
||||
|
||||
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
|
||||
TOY_API void Toy_freeParser(Toy_Parser* parser);
|
||||
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "toy_reffunction.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//memory allocation
|
||||
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||
static Toy_RefFunctionAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
Toy_RefFunction* refFunction = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * length);
|
||||
|
||||
if (refFunction == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//set the data
|
||||
refFunction->refCount = 1;
|
||||
refFunction->length = length;
|
||||
memcpy(refFunction->data, data, refFunction->length);
|
||||
|
||||
return refFunction;
|
||||
}
|
||||
|
||||
void Toy_deleteRefFunction(Toy_RefFunction* refFunction) {
|
||||
//decrement, then check
|
||||
refFunction->refCount--;
|
||||
if (refFunction->refCount <= 0) {
|
||||
allocate(refFunction, sizeof(size_t) + sizeof(int) + sizeof(char) * (refFunction->length + 1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_countRefFunction(Toy_RefFunction* refFunction) {
|
||||
return refFunction->refCount;
|
||||
}
|
||||
|
||||
size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction) {
|
||||
return refFunction->length;
|
||||
}
|
||||
|
||||
Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction) {
|
||||
//Cheaty McCheater Face
|
||||
refFunction->refCount++;
|
||||
return refFunction;
|
||||
}
|
||||
|
||||
Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction) {
|
||||
//create a new function, with a new refCount
|
||||
return Toy_createRefFunction(refFunction->data, refFunction->length);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
//memory allocation hook
|
||||
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
|
||||
|
||||
//the RefFunction structure
|
||||
typedef struct Toy_RefFunction {
|
||||
size_t length;
|
||||
int refCount;
|
||||
unsigned char data[];
|
||||
} Toy_RefFunction;
|
||||
|
||||
//API
|
||||
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
|
||||
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
|
||||
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "toy_refstring.h"
|
||||
|
||||
//memory allocation
|
||||
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||
|
||||
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
Toy_RefString* Toy_createRefString(const char* cstring) {
|
||||
size_t length = strlen(cstring);
|
||||
|
||||
return Toy_createRefStringLength(cstring, length);
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
Toy_RefString* refString = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * (length + 1));
|
||||
|
||||
if (refString == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//set the data
|
||||
refString->refCount = 1;
|
||||
refString->length = length;
|
||||
strncpy(refString->data, cstring, refString->length);
|
||||
|
||||
refString->data[refString->length] = '\0'; //string terminator
|
||||
|
||||
return refString;
|
||||
}
|
||||
|
||||
void Toy_deleteRefString(Toy_RefString* refString) {
|
||||
//decrement, then check
|
||||
refString->refCount--;
|
||||
if (refString->refCount <= 0) {
|
||||
allocate(refString, sizeof(size_t) + sizeof(int) + sizeof(char) * (refString->length + 1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_countRefString(Toy_RefString* refString) {
|
||||
return refString->refCount;
|
||||
}
|
||||
|
||||
size_t Toy_lengthRefString(Toy_RefString* refString) {
|
||||
return refString->length;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_copyRefString(Toy_RefString* refString) {
|
||||
//Cheaty McCheater Face
|
||||
refString->refCount++;
|
||||
return refString;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString) {
|
||||
//create a new string, with a new refCount
|
||||
return Toy_createRefStringLength(refString->data, refString->length);
|
||||
}
|
||||
|
||||
const char* Toy_toCString(Toy_RefString* refString) {
|
||||
return refString->data;
|
||||
}
|
||||
|
||||
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs) {
|
||||
//same pointer
|
||||
if (lhs == rhs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//different length
|
||||
if (lhs->length != rhs->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
|
||||
}
|
||||
|
||||
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
|
||||
//get the rhs length
|
||||
size_t length = strlen(cstring);
|
||||
|
||||
//different length
|
||||
if (lhs->length != length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, cstring, lhs->length) == 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//memory allocation hook
|
||||
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
|
||||
|
||||
//the RefString structure
|
||||
typedef struct Toy_RefString {
|
||||
size_t length;
|
||||
int refCount;
|
||||
char data[];
|
||||
} Toy_RefString;
|
||||
|
||||
//API
|
||||
TOY_API Toy_RefString* Toy_createRefString(const char* cstring);
|
||||
TOY_API Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
|
||||
TOY_API void Toy_deleteRefString(Toy_RefString* refString);
|
||||
TOY_API int Toy_countRefString(Toy_RefString* refString);
|
||||
TOY_API size_t Toy_lengthRefString(Toy_RefString* refString);
|
||||
TOY_API Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
|
||||
TOY_API Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
|
||||
TOY_API const char* Toy_toCString(Toy_RefString* refString);
|
||||
TOY_API bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
|
||||
TOY_API bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
|
||||
|
||||
//TODO: merge refstring memory
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
#include "toy_scope.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
//run up the ancestor chain, freeing anything with 0 references left
|
||||
static void freeAncestorChain(Toy_Scope* scope) {
|
||||
while (scope != NULL) {
|
||||
Toy_Scope* next = scope->ancestor;
|
||||
|
||||
scope->references--;
|
||||
|
||||
if (scope->references <= 0) {
|
||||
Toy_freeLiteralDictionary(&scope->variables);
|
||||
Toy_freeLiteralDictionary(&scope->types);
|
||||
TOY_FREE(Toy_Scope, scope);
|
||||
}
|
||||
|
||||
scope = next;
|
||||
}
|
||||
}
|
||||
|
||||
//return false if invalid type
|
||||
static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal value, bool constCheck) {
|
||||
//for constants, fail if original != value
|
||||
if (constCheck && TOY_AS_TYPE(typeLiteral).constant && !Toy_literalsAreEqual(original, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//for any types
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ANY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//don't allow null types
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//always allow null values
|
||||
if (TOY_IS_NULL(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//for each type, if a mismatch is found, return false
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_BOOLEAN && !TOY_IS_BOOLEAN(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_INTEGER && !TOY_IS_INTEGER(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FLOAT && !TOY_IS_FLOAT(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_STRING && !TOY_IS_STRING(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY && !TOY_IS_ARRAY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_ARRAY(value)) {
|
||||
//check value's type
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new array variable that needs checking
|
||||
if (TOY_IS_NULL(original)) {
|
||||
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
|
||||
if (!checkType( ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check children
|
||||
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
|
||||
if (TOY_AS_ARRAY(original)->count <= i) {
|
||||
return true; //assume new entry pushed
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_AS_ARRAY(original)->literals[i], TOY_AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_DICTIONARY && !TOY_IS_DICTIONARY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_DICTIONARY(value)) {
|
||||
//check value's type
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_DICTIONARY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new dictionary variable that needs checking
|
||||
if (TOY_IS_NULL(original)) {
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
|
||||
//check the type of key and value
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check each child of value against the child of original
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
|
||||
if (TOY_IS_NULL(TOY_AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
|
||||
continue;
|
||||
}
|
||||
|
||||
//find the internal child of original that matches this child of value
|
||||
Toy_private_dictionary_entry* ptr = NULL;
|
||||
|
||||
for (int j = 0; j < TOY_AS_DICTIONARY(original)->capacity; j++) {
|
||||
if (Toy_literalsAreEqual(TOY_AS_DICTIONARY(original)->entries[j].key, TOY_AS_DICTIONARY(value)->entries[i].key)) {
|
||||
ptr = &TOY_AS_DICTIONARY(original)->entries[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if not found, assume it's a new entry
|
||||
if (!ptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//check the type of key and value
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], ptr->key, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], ptr->value, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FUNCTION && !TOY_IS_FUNCTION(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_TYPE && !TOY_IS_TYPE(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_OPAQUE && !TOY_IS_OPAQUE(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
Toy_Scope* Toy_pushScope(Toy_Scope* ancestor) {
|
||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||
scope->ancestor = ancestor;
|
||||
Toy_initLiteralDictionary(&scope->variables);
|
||||
Toy_initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
||||
if (scope == NULL) { //CAN pop a null
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_Scope* ret = scope->ancestor;
|
||||
|
||||
//BUGFIX: when freeing a scope, free the functions' scopes manually - I *think* this is related to the closure hack-in
|
||||
for (int i = 0; i < scope->variables.capacity; i++) {
|
||||
//handle keys, just in case
|
||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].key).scope);
|
||||
TOY_AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
|
||||
}
|
||||
|
||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].value)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].value).scope);
|
||||
TOY_AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
freeAncestorChain(scope);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
|
||||
if (original == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||
scope->ancestor = original->ancestor;
|
||||
Toy_initLiteralDictionary(&scope->variables);
|
||||
Toy_initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
//copy the contents of the dictionaries
|
||||
for (int i = 0; i < original->variables.capacity; i++) {
|
||||
if (!TOY_IS_NULL(original->variables.entries[i].key)) {
|
||||
Toy_setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < original->types.capacity; i++) {
|
||||
if (!TOY_IS_NULL(original->types.entries[i].key)) {
|
||||
Toy_setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
//returns false if error
|
||||
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type) {
|
||||
//don't redefine a variable within this scope
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TOY_IS_TYPE(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//store the type, for later checking on assignment
|
||||
Toy_setLiteralDictionary(&scope->types, key, type);
|
||||
|
||||
Toy_setLiteralDictionary(&scope->variables, key, TOY_TO_NULL_LITERAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
|
||||
while (scope != NULL) {
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
scope = scope->ancestor;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//return false if undefined, or can't be assigned
|
||||
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
|
||||
while (scope != NULL) {
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
scope = scope->ancestor;
|
||||
continue;
|
||||
}
|
||||
|
||||
//type checking
|
||||
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
|
||||
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
|
||||
|
||||
if (!checkType(typeLiteral, original, value, constCheck)) {
|
||||
Toy_freeLiteral(typeLiteral);
|
||||
Toy_freeLiteral(original);
|
||||
return false;
|
||||
}
|
||||
|
||||
//actually assign
|
||||
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
|
||||
|
||||
Toy_freeLiteral(typeLiteral);
|
||||
Toy_freeLiteral(original);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
|
||||
//optimized to reduce call stack
|
||||
while (scope != NULL) {
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
scope = scope->ancestor;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
|
||||
while (scope != NULL) {
|
||||
if (Toy_existsLiteralDictionary(&scope->types, key)) {
|
||||
return Toy_getLiteralDictionary(&scope->types, key);
|
||||
}
|
||||
|
||||
scope = scope->ancestor;
|
||||
}
|
||||
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
typedef struct Toy_Scope {
|
||||
Toy_LiteralDictionary variables; //only allow identifiers as the keys
|
||||
Toy_LiteralDictionary types; //the types, indexed by identifiers
|
||||
struct Toy_Scope* ancestor;
|
||||
int references; //how many scopes point here
|
||||
} Toy_Scope;
|
||||
|
||||
TOY_API Toy_Scope* Toy_pushScope(Toy_Scope* scope);
|
||||
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||
TOY_API Toy_Scope* Toy_copyScope(Toy_Scope* original);
|
||||
|
||||
//returns false if error
|
||||
TOY_API bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
|
||||
TOY_API bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
|
||||
|
||||
//return false if undefined
|
||||
TOY_API bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
|
||||
TOY_API bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
|
||||
|
||||
TOY_API Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum Toy_TokenType {
|
||||
//types
|
||||
TOY_TOKEN_NULL,
|
||||
TOY_TOKEN_BOOLEAN,
|
||||
TOY_TOKEN_INTEGER,
|
||||
TOY_TOKEN_FLOAT,
|
||||
TOY_TOKEN_STRING,
|
||||
TOY_TOKEN_ARRAY,
|
||||
TOY_TOKEN_DICTIONARY,
|
||||
TOY_TOKEN_FUNCTION,
|
||||
TOY_TOKEN_OPAQUE,
|
||||
TOY_TOKEN_ANY,
|
||||
|
||||
//keywords and reserved words
|
||||
TOY_TOKEN_AS,
|
||||
TOY_TOKEN_ASSERT,
|
||||
TOY_TOKEN_BREAK,
|
||||
TOY_TOKEN_CLASS,
|
||||
TOY_TOKEN_CONST,
|
||||
TOY_TOKEN_CONTINUE,
|
||||
TOY_TOKEN_DO,
|
||||
TOY_TOKEN_ELSE,
|
||||
TOY_TOKEN_EXPORT,
|
||||
TOY_TOKEN_FOR,
|
||||
TOY_TOKEN_FOREACH,
|
||||
TOY_TOKEN_IF,
|
||||
TOY_TOKEN_IMPORT,
|
||||
TOY_TOKEN_IN,
|
||||
TOY_TOKEN_OF,
|
||||
TOY_TOKEN_PRINT,
|
||||
TOY_TOKEN_RETURN,
|
||||
TOY_TOKEN_TYPE,
|
||||
TOY_TOKEN_ASTYPE,
|
||||
TOY_TOKEN_TYPEOF,
|
||||
TOY_TOKEN_VAR,
|
||||
TOY_TOKEN_WHILE,
|
||||
|
||||
//literal values
|
||||
TOY_TOKEN_IDENTIFIER,
|
||||
TOY_TOKEN_LITERAL_TRUE,
|
||||
TOY_TOKEN_LITERAL_FALSE,
|
||||
TOY_TOKEN_LITERAL_INTEGER,
|
||||
TOY_TOKEN_LITERAL_FLOAT,
|
||||
TOY_TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
TOY_TOKEN_PLUS,
|
||||
TOY_TOKEN_MINUS,
|
||||
TOY_TOKEN_MULTIPLY,
|
||||
TOY_TOKEN_DIVIDE,
|
||||
TOY_TOKEN_MODULO,
|
||||
TOY_TOKEN_PLUS_ASSIGN,
|
||||
TOY_TOKEN_MINUS_ASSIGN,
|
||||
TOY_TOKEN_MULTIPLY_ASSIGN,
|
||||
TOY_TOKEN_DIVIDE_ASSIGN,
|
||||
TOY_TOKEN_MODULO_ASSIGN,
|
||||
TOY_TOKEN_PLUS_PLUS,
|
||||
TOY_TOKEN_MINUS_MINUS,
|
||||
TOY_TOKEN_ASSIGN,
|
||||
|
||||
//logical operators
|
||||
TOY_TOKEN_PAREN_LEFT,
|
||||
TOY_TOKEN_PAREN_RIGHT,
|
||||
TOY_TOKEN_BRACKET_LEFT,
|
||||
TOY_TOKEN_BRACKET_RIGHT,
|
||||
TOY_TOKEN_BRACE_LEFT,
|
||||
TOY_TOKEN_BRACE_RIGHT,
|
||||
TOY_TOKEN_NOT,
|
||||
TOY_TOKEN_NOT_EQUAL,
|
||||
TOY_TOKEN_EQUAL,
|
||||
TOY_TOKEN_LESS,
|
||||
TOY_TOKEN_GREATER,
|
||||
TOY_TOKEN_LESS_EQUAL,
|
||||
TOY_TOKEN_GREATER_EQUAL,
|
||||
TOY_TOKEN_AND,
|
||||
TOY_TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOY_TOKEN_QUESTION,
|
||||
TOY_TOKEN_COLON,
|
||||
TOY_TOKEN_SEMICOLON,
|
||||
TOY_TOKEN_COMMA,
|
||||
TOY_TOKEN_DOT,
|
||||
TOY_TOKEN_PIPE,
|
||||
TOY_TOKEN_REST,
|
||||
|
||||
//meta tokens
|
||||
TOY_TOKEN_PASS,
|
||||
TOY_TOKEN_ERROR,
|
||||
TOY_TOKEN_EOF,
|
||||
} Toy_TokenType;
|
||||
@@ -0,0 +1,37 @@
|
||||
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) ../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)
|
||||
|
||||
all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
||||
|
||||
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
|
||||
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
|
||||
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
|
||||
valgrind --leak-check=full --track-origins=yes $@
|
||||
else
|
||||
$@
|
||||
endif
|
||||
|
||||
$(OBJ): | $(ODIR)
|
||||
|
||||
$(ODIR):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
@$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
$(ODIR)/%.o: ../source/%.c
|
||||
@$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) $(ODIR)
|
||||
@@ -0,0 +1,49 @@
|
||||
//test operators (integers)
|
||||
assert 1 + 1 == 2, "1 + 1 == 2";
|
||||
assert 1 - 1 == 0, "1 - 1 == 0";
|
||||
assert 2 * 2 == 4, "2 * 2 == 4";
|
||||
assert 1 / 2 == 0, "1 / 2 == 0"; //integer division
|
||||
assert 5 % 2 == 1, "5 % 2 == 1";
|
||||
|
||||
//test operators (floats)
|
||||
assert 1.0 + 1.0 == 2.0, "1.0 + 1.0 == 2.0";
|
||||
assert 1.0 - 1.0 == 0.0, "1.0 - 1.0 == 0.0";
|
||||
assert 2.0 * 2.0 == 4.0, "2.0 * 2.0 == 4.0";
|
||||
assert 1.0 / 2.0 == 0.5, "1.0 / 2.0 == 0.5";
|
||||
|
||||
|
||||
var a = 10;
|
||||
|
||||
a += 20;
|
||||
a -= 25;
|
||||
|
||||
assert a == 5, "+= or -= failed";
|
||||
|
||||
a *= 5;
|
||||
a /= 2;
|
||||
|
||||
assert a == 12, "*= or /= failed";
|
||||
|
||||
a %= 8;
|
||||
|
||||
assert a == 4, "%= failed";
|
||||
|
||||
//strings as special cases
|
||||
var s = "foo";
|
||||
|
||||
assert s + "bar" == "foobar", "string addition failed";
|
||||
assert s == "foo", "string addition failed (was too sticky)";
|
||||
|
||||
s += "bar";
|
||||
|
||||
assert s == "foobar", "string addition failed (wasn't sticky enough)";
|
||||
|
||||
//check order of operations
|
||||
assert 30 / 3 * 2 == 20, "Order of operations failed (raw numbers)";
|
||||
var x = 30;
|
||||
var y = 3;
|
||||
var z = 2;
|
||||
assert x / y * z == 20, "Order of operations failed (variables)";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,23 @@
|
||||
//create a bunch of toy functions as literals to be called from C
|
||||
|
||||
fn answer() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
fn identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
fn makeCounter() {
|
||||
var total = 0;
|
||||
|
||||
fn counter() {
|
||||
return ++total;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
fn fail() {
|
||||
assert false, "Failed correctly";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
var s = "42";
|
||||
var t = "69";
|
||||
|
||||
assert int (s + t) - 1 == 4268, "casting parentheses failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,37 @@
|
||||
//boolean origin
|
||||
var b: bool = true;
|
||||
|
||||
assert bool b == true, "bool -> bool";
|
||||
assert int b == 1, "bool -> int";
|
||||
assert float b == 1, "bool -> float";
|
||||
assert string b == "true", "bool -> string";
|
||||
|
||||
|
||||
//integer origin
|
||||
var i: int = 42;
|
||||
|
||||
assert bool i == true, "int -> bool";
|
||||
assert int i == 42, "int -> int";
|
||||
assert float i == 42, "int -> float";
|
||||
assert string i == "42", "int -> string";
|
||||
|
||||
|
||||
//float origin
|
||||
var f: float = 3.14;
|
||||
|
||||
assert bool f == true, "float -> bool";
|
||||
assert int f == 3, "float -> int";
|
||||
assert float f == 3.14, "float -> float";
|
||||
assert string f == "3.14", "float -> string";
|
||||
|
||||
|
||||
//string origin
|
||||
var s: string = "78.9";
|
||||
|
||||
assert bool s == true, "string -> bool";
|
||||
assert int s == 78, "string -> int";
|
||||
assert float s == 78.9, "string -> float";
|
||||
assert string s == "78.9", "string -> string";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,13 @@
|
||||
//test int -> float coercion
|
||||
{
|
||||
var f: float = 0;
|
||||
|
||||
assert typeof f == float, "coercion on decl failed";
|
||||
|
||||
f = 42;
|
||||
|
||||
assert typeof f == float, "coercion on assign failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,30 @@
|
||||
//test numbers
|
||||
assert 1 < 2, "1 < 2";
|
||||
assert 1 == 1, "1 == 1";
|
||||
assert 2 > 1, "2 > 1";
|
||||
assert 1 <= 2, "1 <= 2";
|
||||
assert 2 >= 1, "2 >= 1";
|
||||
|
||||
|
||||
//test variables
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
|
||||
assert a < b, "a < b";
|
||||
assert a == a, "a == a";
|
||||
assert b > a, "b > a";
|
||||
assert a <= b, "a <= b";
|
||||
assert b >= a, "b >= a";
|
||||
|
||||
|
||||
//test negation
|
||||
assert !false, "!false";
|
||||
|
||||
var c = false;
|
||||
assert !c, "!c";
|
||||
|
||||
//test multiple comparisons
|
||||
assert 1 == 2 == false, "Left-accociative equality failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//dot product
|
||||
var a = [1, 2, 3];
|
||||
var b = [4, 5, 6];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < l1; i++) {
|
||||
push(result, row);
|
||||
}
|
||||
|
||||
//assign the values
|
||||
for (var i = 0; i < length(first); i++) {
|
||||
//select each element of "first"
|
||||
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++) {
|
||||
|
||||
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);
|
||||
|
||||
//result[ i ][ j2 ] += first[i][i2] * second[i2][j2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//matrix multiply
|
||||
var c = [[4], [5], [6]]; //this is a 3x1
|
||||
var d = [[1, 2, 3]]; //this is a 1x3
|
||||
|
||||
// c x d = 3x3
|
||||
// d x c = 1x1
|
||||
|
||||
assert matrix(c, d) == [[4,8,12],[5,10,15],[6,12,18]], "Matrix multiplication failed";
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
|
||||
NOTES: For some reason, this code results in the error:
|
||||
Undeclared variable "inner"
|
||||
|
||||
It only occurs under these very specific conditions.
|
||||
|
||||
It appears to be a compiler issue, see issue #38 for more info.
|
||||
|
||||
*/
|
||||
|
||||
fn getValue(self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
var cache;
|
||||
cache = 42.getValue(); //assignment, rather than declaration, allows the bug
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,30 @@
|
||||
//test function chaining with the dot operator
|
||||
|
||||
fn identity(self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
fn check(self) {
|
||||
assert self == 42, "dot chaining failed";
|
||||
return self;
|
||||
}
|
||||
|
||||
var val = 42;
|
||||
|
||||
val
|
||||
.identity()
|
||||
.check()
|
||||
.identity()
|
||||
.check()
|
||||
;
|
||||
|
||||
|
||||
//test the value is actually altered
|
||||
fn increment(self) {
|
||||
return self + 1;
|
||||
}
|
||||
|
||||
assert 3.increment().increment() == 5, "dot chaining increment failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,23 @@
|
||||
var days = [
|
||||
"sunday",
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday"
|
||||
];
|
||||
|
||||
var rng = 10; //for chosen at random
|
||||
|
||||
|
||||
var index = rng % days.length();
|
||||
|
||||
assert index == 3, "dot modulo bugfix failed";
|
||||
|
||||
rng %= days.length();
|
||||
|
||||
assert rng == 3, "dot modulo assign bugfix failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
fn add(self, inc) {
|
||||
return self + inc;
|
||||
}
|
||||
|
||||
assert 1.add(2).add(3).add(4) == 10, "dottify bugfix failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
fn a() {
|
||||
fn b() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
assert a()() == 42, "function within function failed";
|
||||
}
|
||||
|
||||
{
|
||||
fn a() {
|
||||
fn b() {
|
||||
fn c() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
assert a()()() == 42, "function within function within function failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,77 @@
|
||||
//test function return
|
||||
fn testFourtyTwo() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
assert testFourtyTwo() == 42, "function returns failed";
|
||||
|
||||
|
||||
//test function parameters
|
||||
fn identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
assert identity("hello world") == "hello world", "identity function failed";
|
||||
|
||||
|
||||
//test closures
|
||||
fn make() {
|
||||
var counter = 0;
|
||||
|
||||
fn count() {
|
||||
return ++counter;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
var tally = make();
|
||||
|
||||
assert tally() == 1 && tally() == 2, "Closures failed";
|
||||
|
||||
|
||||
//test closures self-capture
|
||||
fn capture(count: int) {
|
||||
if (count > 5) {
|
||||
return count;
|
||||
}
|
||||
|
||||
return capture(count + 1);
|
||||
}
|
||||
|
||||
assert capture(0) == 6, "Self capture failed";
|
||||
|
||||
|
||||
//test expressions as arguments
|
||||
fn argFn() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
fn outerFn(val) {
|
||||
assert val == 42, "expression as argument failed";
|
||||
}
|
||||
|
||||
outerFn(argFn());
|
||||
|
||||
|
||||
//test extra parameters
|
||||
fn extra(one, two, ...rest) {
|
||||
assert rest == ["three", "four", "five", "six", "seven"], "rest parameters failed";
|
||||
}
|
||||
|
||||
extra("one", "two", "three", "four", "five", "six", "seven");
|
||||
|
||||
|
||||
//test underscore functions
|
||||
fn example(self, a, b, c) {
|
||||
assert a == "a", "underscore failed (a)";
|
||||
assert b == "b", "underscore failed (b)";
|
||||
assert c == "c", "underscore failed (c)";
|
||||
return self;
|
||||
}
|
||||
|
||||
assert "hello world".example("a", "b", "c") == "hello world", "underscore call failed";
|
||||
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
//test basic indexing
|
||||
{
|
||||
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||
|
||||
assert week[1] == "tuesday", "basic indexing failed (single element)";
|
||||
|
||||
assert week[1:1] == ["tuesday"], "basic indexing failed (single element as array)";
|
||||
|
||||
assert week[:] == ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"], "basic default indexing failed (first and second)";
|
||||
assert week[::] == ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"], "basic default indexing failed (first, second and third)";
|
||||
}
|
||||
|
||||
|
||||
//test basic replacement
|
||||
{
|
||||
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||
|
||||
week[3:3] = "Holiday";
|
||||
|
||||
assert week == ["monday", "tuesday", "wednesday", "Holiday", "friday", "saturday", "sunday"], "basic replacement failed";
|
||||
}
|
||||
|
||||
|
||||
//test backwards
|
||||
{
|
||||
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||
|
||||
assert week[::-1] == ["sunday", "saturday", "friday", "thursday", "wednesday", "tuesday", "monday"], "backwards failed";
|
||||
}
|
||||
|
||||
|
||||
//test array weird manipulation
|
||||
{
|
||||
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||
|
||||
week[::-2] = ["first", "second", "third"];
|
||||
|
||||
assert week == ["monday", "tuesday", "third", "thursday", "second", "saturday", "first"], "array weird manipulation failed";
|
||||
}
|
||||
|
||||
|
||||
//test index arithmetic
|
||||
{
|
||||
var a = [1, 2, 3];
|
||||
|
||||
a[1] *= 3;
|
||||
|
||||
assert a == [1, 6, 3], "index arithmetic failed";
|
||||
}
|
||||
|
||||
|
||||
//test indexing with variables
|
||||
{
|
||||
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||
|
||||
var first = 1;
|
||||
var second = 2;
|
||||
var third = -1;
|
||||
|
||||
assert week[first:second:third] == ["wednesday", "tuesday"], "indexing with variables failed";
|
||||
}
|
||||
|
||||
|
||||
//test nested indexing
|
||||
{
|
||||
var a = [[[0]]];
|
||||
|
||||
a[0][0][0] = 42;
|
||||
|
||||
assert a[0][0] == [42], "nested indexing failed";
|
||||
}
|
||||
|
||||
|
||||
//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) {
|
||||
return [a, b, c];
|
||||
}
|
||||
|
||||
assert combine(1, 2, 3) == [1, 2, 3], "combine example failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -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";
|
||||