Compare commits
283 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b602e2ff87 | |||
| 0b99eb7b0c | |||
| 2505cedc79 | |||
| d7035a59c8 | |||
| ea584d8950 | |||
| 2ce9a0cf42 | |||
| b77f0fb50d | |||
| edb5a52562 | |||
| 9dc9316853 | |||
| a864a1a226 | |||
| c645026620 | |||
| a9ccd65da1 | |||
| 0da5201829 | |||
| 6be29ed8c5 | |||
| 6341d3337f | |||
| d4f952eafc | |||
| d5bc07d3b3 | |||
| 5a851f6fbe | |||
| d8c6a3ec27 | |||
| b5883e248b | |||
| 52048f2466 | |||
| 6b8e95d250 | |||
| 5721edc2d1 | |||
| db52c13613 | |||
| 7290efe069 | |||
| 0cf92bdeae | |||
| 4c9a2e5378 | |||
| 1e11e9eea7 | |||
| cce8ae1ea3 | |||
| ce54912232 | |||
| 23b55fc360 | |||
| 62fe86f99b | |||
| 401de578a5 | |||
| fb4258f9df | |||
| f885fdaf4c | |||
| cfec1b6911 | |||
| a63bdaef1c | |||
| 3783c94064 | |||
| d292b33184 | |||
| 64944c24f6 | |||
| 604604e8bc | |||
| 67e49b7477 | |||
| 967963c9d7 | |||
| 9b469e6eb0 | |||
| f8094fa17e | |||
| 8714c56c3e | |||
| 9faaa311e0 | |||
| f5ba1181c0 | |||
| b06b2d9485 | |||
| e3e9ca7ece | |||
| 81fe278c96 | |||
| 027d093e21 | |||
| 2eaf7fc71a | |||
| c43310f316 | |||
| 6e07c5f2f4 | |||
| 7690dce3f6 | |||
| 1ed1993489 | |||
| 9b5327b83d | |||
| 10dbe8f8f1 | |||
| 9e4ad7a9a5 | |||
| 5317a12383 | |||
| 35bfa1b9f1 | |||
| 7f692b4cb4 | |||
| 0cef0abdb5 | |||
| 6ba42b5a9b | |||
| 3cb62274c9 | |||
| 60b561d809 | |||
| 70b2dcd829 | |||
| 0955b3ff38 | |||
| 4137935468 | |||
| ebeabcb9d4 | |||
| 4d33a9473a | |||
| 61e3cdba82 | |||
| 3b7d2be87e | |||
| fa175203c9 | |||
| b4a3e9b42b | |||
| 6347778ead | |||
| ba98624e82 | |||
| 24ce965e76 | |||
| 29c130135c | |||
| 01eca37560 | |||
| 8e5ec7d847 | |||
| 9ed6383630 | |||
| 1ec0f63f76 | |||
| d2341ae227 | |||
| 3782f2aaaa | |||
| b636ab9e31 | |||
| cdfe17ad53 | |||
| 3d7d1179c9 | |||
| c3c46b4fc6 | |||
| 0e41b00ef4 | |||
| f6ec6a8c73 | |||
| 2157b2f540 | |||
| 1481216e69 | |||
| f25f389b4e | |||
| deff784df8 | |||
| 54e82846c3 | |||
| 67fce427eb | |||
| 8a2cb61435 | |||
| 50d03e28fc | |||
| 763581c73b | |||
| cdb2613e5d | |||
| 733df87c08 | |||
| bfd506f497 | |||
| 18b59c9e84 | |||
| d3eb31d964 | |||
| 07f4a98b95 | |||
| 0949fd6ff9 | |||
| 03e5096f10 | |||
| bb81b8c474 | |||
| cf6db57787 | |||
| 17f0e4476b | |||
| 1095e1a885 | |||
| 2edfbbe3ef | |||
| 4b83f1f0d6 | |||
| e2fa1cf2e8 | |||
| a04d2c4816 | |||
| f2f8aed23a | |||
| 68ed52b347 | |||
| 88dac53ae0 | |||
| f84cdff883 | |||
| f869c9425a | |||
| 76ddd5703e | |||
| 669808730e | |||
| e6d9809da5 | |||
| 502032e514 | |||
| 6e9d42f892 | |||
| 70ca27486e | |||
| 12fa434e0f | |||
| efc1e764d2 | |||
| c5c0122243 | |||
| 348b7b8c24 | |||
| e243ad949a | |||
| 9b673f23ad | |||
| 624a0c80ba | |||
| 1064b69d04 | |||
| e9b347acb6 | |||
| 071c8da2aa | |||
| d6538812bf | |||
| 3aeddff736 | |||
| c88c1b125d | |||
| 1513ba9878 | |||
| bc0289c3f4 | |||
| 92c71a374d | |||
| e0547474b8 | |||
| 3e6d21afbb | |||
| d3df01c1c4 | |||
| cdca6fa45c | |||
| 1dde9d8f29 | |||
| 7f0f17b6e0 | |||
| 3507104121 | |||
| 87de634e30 | |||
| 6fa224fa7b | |||
| 8a68d864e6 | |||
| 49f240ea07 | |||
| 3acbd7447a | |||
| 6f126e6daa | |||
| 2adb9d9158 | |||
| 1668dca255 | |||
| 501ff6fff4 | |||
| 3845627fe5 | |||
| cdae03bd54 | |||
| 7b501b71b5 | |||
| 913738a4d1 | |||
| 3312a38c7c | |||
| 71b57fd42c | |||
| 453afbab41 | |||
| 57af5a6d59 | |||
| 0737b2a483 | |||
| eae96d6403 | |||
| b55b8e879e | |||
| 1ed114b80d | |||
| eb8e522bf2 | |||
| 16b71ba6f4 | |||
| 9725f3c6a3 | |||
| 8653a2663f | |||
| ab2cd5dc93 | |||
| 724804a78a | |||
| 77a128e0f7 | |||
| 5343e1054d | |||
| 3930ec0477 | |||
| 996744d7ec | |||
| c00b32017b | |||
| 457014d577 | |||
| be4cbf1ad6 | |||
| aeb008c684 | |||
| 53012dbce1 | |||
| 4fe57f9562 | |||
| 3ba2e420ea | |||
| c81a139c97 | |||
| 66ea684a90 | |||
| a26a6a56d0 | |||
| ee226ea426 | |||
| 76a0290290 | |||
| 85dc24c4a6 | |||
| 1804e380a0 | |||
| 7567f4f3b2 | |||
| e8160eb9df | |||
| 276648630e | |||
| e946a5f071 | |||
| 932401c26d | |||
| fc67d6a18b | |||
| 60908c8bf3 | |||
| b8e3324acb | |||
| 0fc8183799 | |||
| 9a6aa8d15e | |||
| 0b9051cab2 | |||
| 0d7e4db661 | |||
| d5c833b344 | |||
| c875ae7a0e | |||
| c0ec5ef28e | |||
| be91de9535 | |||
| 3088c4fe6d | |||
| 9bd0cd23e7 | |||
| 29f8a698b4 | |||
| 41d274177a | |||
| 7ea249f723 | |||
| 3949be3706 | |||
| 0e932f24cc | |||
| 60225b733b | |||
| 15f99495a1 | |||
| 7b26527e95 | |||
| 386201b6e9 | |||
| 8d278077b1 | |||
| 57c16d2ede | |||
| 2f1613e306 | |||
| 2776c87026 | |||
| 8cc33f5fbb | |||
| 90f91a234a | |||
| fcd1cdf70b | |||
| e265038547 | |||
| a357e0b0d4 | |||
| 2c5e3c6aa5 | |||
| aeda0a0d94 | |||
| ebbcba1b16 | |||
| 4bce10803e | |||
| daee91904d | |||
| c397384766 | |||
| 667efc2feb | |||
| f4469fc53d | |||
| c86c5800a7 | |||
| e4d843ad3a | |||
| 6667bce3d7 | |||
| 50b07da188 | |||
| 708db75760 | |||
| b632ce77ba | |||
| b2c3dd894c | |||
| 9b21bfb53b | |||
| d3adac8a59 | |||
| 5183037a99 | |||
| 2e2bee4fa3 | |||
| 047ccc5f16 | |||
| 59b0d15915 | |||
| 6d961cea78 | |||
| 33f360c9cf | |||
| d3516b4fc9 | |||
| df85d30553 | |||
| d57fca474f | |||
| 69f5151310 | |||
| c47ee68b3f | |||
| 2c06c5e38b | |||
| e6e24ca19f | |||
| 390f60e0ce | |||
| 580e407b05 | |||
| 26bd476c2c | |||
| b8b67fce3d | |||
| 68ed39fc45 | |||
| aeecfabbbc | |||
| 51740e2b9e | |||
| 402abb647c | |||
| 3aa7bf1877 | |||
| ae1dc5841e | |||
| 4b60d65203 | |||
| 3498baad9b | |||
| 0649a141dd | |||
| 65e5905011 | |||
| bf64275aa9 | |||
| 08b400debf | |||
| 9ad2a6cf2e | |||
| 8009f410a4 | |||
| 584fb115b6 | |||
| 70698a4a1a | |||
| eb26d23363 |
@@ -0,0 +1,5 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
patreon: krgamestudios
|
||||||
|
ko_fi: krgamestudios
|
||||||
|
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
labels: bug
|
||||||
|
---
|
||||||
|
|
||||||
|
## Describe the bug
|
||||||
|
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
## To Reproduce
|
||||||
|
|
||||||
|
Steps to reproduce the behaviour:
|
||||||
|
|
||||||
|
1. run `git pull` on the repository
|
||||||
|
2. run `make rebuild` on the code
|
||||||
|
3. ...
|
||||||
|
|
||||||
|
You can include some screenshots here if you'd like!
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
- OS: [for example MacOS, Windows, iOS, Android]
|
||||||
|
- Version: [What version of Toy was this running?]
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
|
Add any other context about the problem here.
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request
|
||||||
|
about: Suggest an idea
|
||||||
|
labels: enhancement
|
||||||
|
---
|
||||||
|
|
||||||
|
### Describe the feature you’d like
|
||||||
|
|
||||||
|
A clear and concise description of what you’d like to be able to do with Toy.
|
||||||
|
|
||||||
|
### Describe alternatives you've considered
|
||||||
|
|
||||||
|
A clear and concise description of any alternative solutions or workarounds you've considered.
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
|
Add any other context about the feature request here.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: Question
|
||||||
|
about: Ask a Question
|
||||||
|
labels: question
|
||||||
|
---
|
||||||
|
|
||||||
|
### How can I help?
|
||||||
|
|
||||||
|
I'm always here to help with any inquiries you have regarding Toy and its related projects.
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
name: Comprehensive Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main", "dev" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: install valgrind
|
|
||||||
run: sudo apt install valgrind
|
|
||||||
- name: make test
|
|
||||||
run: make test
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: Continuous Integration v1.x
|
||||||
|
|
||||||
|
#trigger when these occur
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- v1
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
branches:
|
||||||
|
- v1
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
#testing the CI workflows under multiple supported conditions
|
||||||
|
jobs:
|
||||||
|
test-valgrind:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- 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@v4
|
||||||
|
- name: make test (sanitized)
|
||||||
|
run: make test-sanitized
|
||||||
|
|
||||||
|
test-mingw32:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: make test (mingw32)
|
||||||
|
run: make test
|
||||||
+56
-28
@@ -1,31 +1,59 @@
|
|||||||
#Editor generated files
|
# Prerequisites
|
||||||
*.sln
|
*.d
|
||||||
*.vcproj
|
|
||||||
*.suo
|
|
||||||
*.ncb
|
|
||||||
*.user
|
|
||||||
compile_commands.json
|
|
||||||
|
|
||||||
#Directories
|
# Object files
|
||||||
Release/
|
|
||||||
Debug/
|
|
||||||
Out/
|
|
||||||
release/
|
|
||||||
debug/
|
|
||||||
out/
|
|
||||||
.cache/
|
|
||||||
|
|
||||||
#Project generated files
|
|
||||||
*.db
|
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.ko
|
||||||
*.exe
|
*.obj
|
||||||
*.meta
|
*.elf
|
||||||
*.log
|
|
||||||
out
|
|
||||||
*.stackdump
|
|
||||||
*.tb
|
|
||||||
|
|
||||||
#Shell files
|
# Linker output
|
||||||
*.bat
|
*.ilk
|
||||||
*.sh
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
||||||
|
.cproject
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
temp/
|
||||||
|
Release/
|
||||||
|
out/
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# License
|
# License
|
||||||
|
|
||||||
Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios
|
Copyright (c) 2020-2024 Kayne Ruse, KR Game Studios
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
|||||||
@@ -2,30 +2,32 @@
|
|||||||
<image src="toylogo.png" />
|
<image src="toylogo.png" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Toy
|
# Toy v1
|
||||||
|
|
||||||
|
The Toy programming language is an imperative bytecode-intermediate embedded scripting language. It isn't intended to operate on its own, but rather as part of another program, the "host". This process is intended to allow a decent amount of easy customisation by the host's end user, by exposing logic in script files. Alternatively, binary files in a custom format can be used as well.
|
||||||
|
|
||||||
|
The host will provide all of the extensions needed on a case-by-case basis. Script files have the `.toy` file extension, while binary files have the `.tb` file extension.
|
||||||
|
|
||||||
This is the Toy programming language interpreter, written in C.
|
This is the Toy programming language interpreter, written in C.
|
||||||
|
|
||||||
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
|
||||||
|
|
||||||
# Nifty Features
|
# Nifty Features
|
||||||
|
|
||||||
* Simple C-like syntax
|
* Simple C-like syntax
|
||||||
* Bytecode intermediate compilation
|
* Bytecode intermediate compilation
|
||||||
* Optional, but robust type system (including `opaque` for arbitrary data)
|
* Optional, but robust type system (including `opaque` for arbitrary data)
|
||||||
* Functions and types are first-class citizens
|
* Functions and types are first-class citizens
|
||||||
* `import` and `export` variables from the host program
|
* Import native libraries from the host
|
||||||
* Fancy slice notation for strings, arrays and dictionaries
|
* Fancy slice notation for strings, arrays and dictionaries
|
||||||
* Can re-direct output, error and assertion failure messages
|
* Can re-direct output, error and assertion failure messages
|
||||||
* Open source under the zlib license
|
* Open source under the zlib license
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
For Windows, Linux and MacOS, simply run `make` in the root directory.
|
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
|
||||||
|
|
||||||
Note: For Linux, you may need to `cd` into the `out` directory before running.
|
For Windows(MSVC), Visual Studio project files are included.
|
||||||
|
|
||||||
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
|
Note: MacOS and Windows(MSVC) are not officially supported, but we'll do our best!
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
@@ -33,12 +35,13 @@ Run `make install-tools` to install a number of tools, including:
|
|||||||
|
|
||||||
* VSCode syntax highlighting
|
* VSCode syntax highlighting
|
||||||
|
|
||||||
|
Other tools such as a disassembler are available, as well - simply run `make` in the correct directory.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
```
|
```
|
||||||
import standard; //for a bunch of utility functions
|
import standard; //for a bunch of utility functions
|
||||||
|
|
||||||
|
|
||||||
print "Hello world"; //"print" is a keyword
|
print "Hello world"; //"print" is a keyword
|
||||||
|
|
||||||
var msg = "foobar"; //declare a variable like this
|
var msg = "foobar"; //declare a variable like this
|
||||||
@@ -62,10 +65,22 @@ var tally = makeCounter();
|
|||||||
print tally(); //1
|
print tally(); //1
|
||||||
print tally(); //2
|
print tally(); //2
|
||||||
print tally(); //3
|
print tally(); //3
|
||||||
|
|
||||||
export tally; //export this variable to the host program
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
|
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
|
||||||
|
|
||||||
|
# Contributions
|
||||||
|
|
||||||
|
@hiperiondev - Disassembler, porting support and feedback
|
||||||
|
@add00 - Library support
|
||||||
|
@gruelingpine185 - Unofficial MacOS support
|
||||||
|
@solar-mist - Minor bugfixes
|
||||||
|
Unnamed Individuals - Feedback
|
||||||
|
|
||||||
|
# Patrons via Patreon
|
||||||
|
|
||||||
|
* Seth A. Robinson
|
||||||
|
|
||||||
|
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
||||||
|
|||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>17.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{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\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(SolutionDir)out\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</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</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</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Toy.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="Toy.vcxproj">
|
||||||
|
<Project>{26360002-cc2a-469a-9b28-ba0c1af41657}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="repl\drive_system.c" />
|
||||||
|
<ClCompile Include="repl\lib_math.c" />
|
||||||
|
<ClCompile Include="repl\lib_random.c" />
|
||||||
|
<ClCompile Include="repl\lib_runner.c" />
|
||||||
|
<ClCompile Include="repl\lib_standard.c" />
|
||||||
|
<ClCompile Include="repl\lib_toy_version_info.c" />
|
||||||
|
<ClCompile Include="repl\repl_main.c" />
|
||||||
|
<ClCompile Include="repl\repl_tools.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="repl\drive_system.h" />
|
||||||
|
<ClInclude Include="repl\lib_math.h" />
|
||||||
|
<ClInclude Include="repl\lib_random.h" />
|
||||||
|
<ClInclude Include="repl\lib_runner.h" />
|
||||||
|
<ClInclude Include="repl\lib_standard.h" />
|
||||||
|
<ClInclude Include="repl\lib_toy_version_info.h" />
|
||||||
|
<ClInclude Include="repl\repl_tools.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
+184
@@ -0,0 +1,184 @@
|
|||||||
|
<?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\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(SolutionDir)out\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<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>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>
|
||||||
|
</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
<Link>
|
||||||
|
<OutputFile>$(Outdir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
</Link>
|
||||||
|
<Bscmake>
|
||||||
|
<OutputFile>$(Platform)\$(Configuration)\$(TargetName).bsc</OutputFile>
|
||||||
|
</Bscmake>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
|
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>
|
||||||
|
</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
<Link>
|
||||||
|
<OutputFile>$(Outdir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
</Link>
|
||||||
|
<Bscmake>
|
||||||
|
<OutputFile>$(Platform)\$(Configuration)\$(TargetName).bsc</OutputFile>
|
||||||
|
</Bscmake>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="source\toy_ast_node.c" />
|
||||||
|
<ClCompile Include="source\toy_builtin.c" />
|
||||||
|
<ClCompile Include="source\toy_common.c" />
|
||||||
|
<ClCompile Include="source\toy_compiler.c" />
|
||||||
|
<ClCompile Include="source\toy_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_interpreter.h" />
|
||||||
|
<ClInclude Include="source\toy_keyword_types.h" />
|
||||||
|
<ClInclude Include="source\toy_lexer.h" />
|
||||||
|
<ClInclude Include="source\toy_literal.h" />
|
||||||
|
<ClInclude Include="source\toy_literal_array.h" />
|
||||||
|
<ClInclude Include="source\toy_literal_dictionary.h" />
|
||||||
|
<ClInclude Include="source\toy_memory.h" />
|
||||||
|
<ClInclude Include="source\toy_opcodes.h" />
|
||||||
|
<ClInclude Include="source\toy_parser.h" />
|
||||||
|
<ClInclude Include="source\toy_reffunction.h" />
|
||||||
|
<ClInclude Include="source\toy_refstring.h" />
|
||||||
|
<ClInclude Include="source\toy_scope.h" />
|
||||||
|
<ClInclude Include="source\toy_token_types.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.4.33213.308
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Toy", "Toy.vcxproj", "{26360002-CC2A-469A-9B28-BA0C1AF41657}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Repl", "Repl.vcxproj", "{97F823E5-3AB8-47EF-B142-C15DD7CADF76}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657} = {26360002-CC2A-469A-9B28-BA0C1AF41657}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.Build.0 = Release|x64
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.Build.0 = Release|x64
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {7089F1AD-8EC0-4F27-AFD1-5FD43D91AABC}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
# Optimisation Options
|
export CFLAGS+=-std=c18 -pedantic -Werror
|
||||||
# export CFLAGS+=-O2 -mtune=native -march=native
|
|
||||||
|
|
||||||
export TOY_OUTDIR = out
|
export TOY_OUTDIR = out
|
||||||
|
|
||||||
@@ -7,10 +6,10 @@ all: $(TOY_OUTDIR) repl
|
|||||||
|
|
||||||
#repl builds
|
#repl builds
|
||||||
repl: $(TOY_OUTDIR) library
|
repl: $(TOY_OUTDIR) library
|
||||||
$(MAKE) -C repl
|
$(MAKE) -j8 -C repl
|
||||||
|
|
||||||
repl-static: $(TOY_OUTDIR) static
|
repl-static: $(TOY_OUTDIR) static
|
||||||
$(MAKE) -C repl
|
$(MAKE) -j8 -C repl
|
||||||
|
|
||||||
repl-release: clean $(TOY_OUTDIR) library-release
|
repl-release: clean $(TOY_OUTDIR) library-release
|
||||||
$(MAKE) -C repl release
|
$(MAKE) -C repl release
|
||||||
@@ -25,16 +24,26 @@ library: $(TOY_OUTDIR)
|
|||||||
static: $(TOY_OUTDIR)
|
static: $(TOY_OUTDIR)
|
||||||
$(MAKE) -j8 -C source static
|
$(MAKE) -j8 -C source static
|
||||||
|
|
||||||
library-release: $(TOY_OUTDIR)
|
library-release: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -j8 -C source library-release
|
$(MAKE) -j8 -C source library-release
|
||||||
|
|
||||||
static-release: $(TOY_OUTDIR)
|
static-release: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -j8 -C source static-release
|
$(MAKE) -j8 -C source static-release
|
||||||
|
|
||||||
|
#distribution
|
||||||
|
dist: export CFLAGS+=-O2 -mtune=native -march=native
|
||||||
|
dist: repl-release
|
||||||
|
|
||||||
#utils
|
#utils
|
||||||
test: clean $(TOY_OUTDIR)
|
test: clean $(TOY_OUTDIR)
|
||||||
$(MAKE) -C test
|
$(MAKE) -C test
|
||||||
|
|
||||||
|
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):
|
$(TOY_OUTDIR):
|
||||||
mkdir $(TOY_OUTDIR)
|
mkdir $(TOY_OUTDIR)
|
||||||
|
|
||||||
@@ -42,6 +51,25 @@ $(TOY_OUTDIR):
|
|||||||
install-tools:
|
install-tools:
|
||||||
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
|
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
|
||||||
|
|
||||||
|
#utils
|
||||||
|
build-mecha: $(TOY_OUTDIR)
|
||||||
|
g++ -o $(TOY_OUTDIR)/mecha tools/mecha.cpp
|
||||||
|
|
||||||
|
build-docs: build-mecha
|
||||||
|
$(TOY_OUTDIR)/mecha $(wildcard source/*.h)
|
||||||
|
$(TOY_OUTDIR)/mecha $(wildcard repl/*.h)
|
||||||
|
|
||||||
|
docs:
|
||||||
|
mkdir docs
|
||||||
|
|
||||||
|
move-docs: docs
|
||||||
|
mv -u $(wildcard source/*.md) docs
|
||||||
|
mv -u $(wildcard repl/*.md) docs
|
||||||
|
|
||||||
|
documentation:
|
||||||
|
$(MAKE) build-docs
|
||||||
|
$(MAKE) move-docs
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#include "drive_system.h"
|
||||||
|
|
||||||
|
#include "toy_memory.h"
|
||||||
|
#include "toy_literal_dictionary.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//file system API
|
||||||
|
static Toy_LiteralDictionary driveDictionary;
|
||||||
|
|
||||||
|
void Toy_initDriveSystem() {
|
||||||
|
Toy_initLiteralDictionary(&driveDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_freeDriveSystem() {
|
||||||
|
Toy_freeLiteralDictionary(&driveDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_setDrivePath(char* drive, char* path) {
|
||||||
|
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
|
||||||
|
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
|
||||||
|
|
||||||
|
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
|
||||||
|
|
||||||
|
Toy_freeLiteral(driveLiteral);
|
||||||
|
Toy_freeLiteral(pathLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
|
||||||
|
//check argument types
|
||||||
|
if (!TOY_IS_STRING(*drivePathLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
|
||||||
|
|
||||||
|
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||||
|
size_t driveLength = 0;
|
||||||
|
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||||
|
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||||
|
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
|
||||||
|
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
driveLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||||
|
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||||
|
|
||||||
|
//get the real drive file path
|
||||||
|
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||||
|
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
|
||||||
|
|
||||||
|
if (!TOY_IS_STRING(pathLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||||
|
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
|
||||||
|
interpreter->errorOutput("\n");
|
||||||
|
Toy_freeLiteral(driveLiteral);
|
||||||
|
Toy_freeLiteral(pathLiteral);
|
||||||
|
Toy_deleteRefString(filePath);
|
||||||
|
Toy_deleteRefString(drivePath);
|
||||||
|
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||||
|
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
|
||||||
|
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
|
||||||
|
|
||||||
|
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
|
||||||
|
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
|
||||||
|
|
||||||
|
//clean up the drive/path stuff
|
||||||
|
Toy_deleteRefString(drivePath);
|
||||||
|
Toy_deleteRefString(filePath);
|
||||||
|
Toy_deleteRefString(path);
|
||||||
|
Toy_freeLiteral(driveLiteral);
|
||||||
|
Toy_freeLiteral(pathLiteral);
|
||||||
|
|
||||||
|
//check for break-out attempts
|
||||||
|
for (size_t i = 0; i < fileLength - 1; i++) {
|
||||||
|
if (file[i] == '.' && file[i + 1] == '.') {
|
||||||
|
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||||
|
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||||
|
return TOY_TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
|
||||||
|
|
||||||
|
TOY_FREE_ARRAY(char, file, fileLength + 1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# drive_system.h
|
||||||
|
|
||||||
|
When accessing the file system through Toy (such as with the runner library), it's best practice to utilize the drive system - this system (tries to) prevent malicious accessing of files outside of the designated folders. It does this by causing an error when a script tries to access a parent directory.
|
||||||
|
|
||||||
|
To use the drive system, first you must designate specific folders which can be accessed, like so:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "drive_system.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
//the drive system uses a LiteralDictionary, which must be initialized with this
|
||||||
|
Toy_initDriveSystem();
|
||||||
|
|
||||||
|
Toy_setDrivePath("scripts", "assets/scripts");
|
||||||
|
Toy_setDrivePath("sprites", "assets/sprites");
|
||||||
|
Toy_setDrivePath("fonts", "assets/fonts");
|
||||||
|
|
||||||
|
//TODO: do you stuff here
|
||||||
|
|
||||||
|
//clean up the drive dictionary when you're done
|
||||||
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This utility is intended mainly for libraries to use - as such, the core of Toy does not utilize it.
|
||||||
|
|
||||||
|
### Implementation Details
|
||||||
|
|
||||||
|
The drive system uses a Toy's Dictionary structure to store the mappings between keys and values - this dictionary object is a static global which persists for the lifetime of the program.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_literal.h"
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_initDriveSystem()
|
||||||
|
|
||||||
|
This function initializes the drive system.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_initDriveSystem();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeDriveSystem()
|
||||||
|
|
||||||
|
This function cleans up after the drive system is no longer needed.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeDriveSystem();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setDrivePath(char* drive, char* path)
|
||||||
|
|
||||||
|
This function sets a key-value pair in the drive system. It uses C strings, since its intended to be called directly from `main()`.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setDrivePath(char* drive, char* path);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral)
|
||||||
|
|
||||||
|
This function, when given a string literal of the correct format, will return a new string literal containing the relative filepath to a specified file.
|
||||||
|
|
||||||
|
The correct format is `drive:/path/to/filename`, where `drive` is a drive that was specified with `Toy_setDrivePath()`.
|
||||||
|
|
||||||
|
On failure, this function returns a null literal.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
|
||||||
+1152
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
int Toy_hookMath(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#include "lib_random.h"
|
||||||
|
|
||||||
|
#include "toy_memory.h"
|
||||||
|
|
||||||
|
static int hashInt(int x) {
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = (x >> 16) ^ x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct Toy_RandomGenerator {
|
||||||
|
int seed; //mutated with each call
|
||||||
|
} Toy_RandomGenerator;
|
||||||
|
|
||||||
|
//Toy native functions
|
||||||
|
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the seed argument
|
||||||
|
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Toy_Literal seedLiteralIdn = seedLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
|
||||||
|
Toy_freeLiteral(seedLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TOY_IS_INTEGER(seedLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
|
||||||
|
Toy_freeLiteral(seedLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate the generator object
|
||||||
|
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
|
||||||
|
generator->seed = TOY_AS_INTEGER(seedLiteral);
|
||||||
|
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
|
||||||
|
|
||||||
|
//return and cleanup
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
|
||||||
|
|
||||||
|
Toy_freeLiteral(seedLiteral);
|
||||||
|
Toy_freeLiteral(generatorLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the runner object
|
||||||
|
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||||
|
Toy_freeLiteral(generatorLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||||
|
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||||
|
|
||||||
|
//generate the new value and package up the return
|
||||||
|
generator->seed = hashInt(generator->seed);
|
||||||
|
|
||||||
|
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
|
||||||
|
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeLiteral(generatorLiteral);
|
||||||
|
Toy_freeLiteral(resultLiteral);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the runner object
|
||||||
|
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Toy_Literal generatorLiteralIdn = generatorLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
|
||||||
|
Toy_freeLiteral(generatorLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
|
||||||
|
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
|
||||||
|
|
||||||
|
//clear out the runner object
|
||||||
|
TOY_FREE(Toy_RandomGenerator, generator);
|
||||||
|
Toy_freeLiteral(generatorLiteral);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
const char* name;
|
||||||
|
Toy_NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"createRandomGenerator", nativeCreateRandomGenerator},
|
||||||
|
{"generateRandomNumber", nativeGenerateRandomNumber},
|
||||||
|
{"freeRandomGenerator", nativeFreeRandomGenerator},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!TOY_IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
Toy_freeLiteral(alias);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||||
|
Toy_initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||||
|
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||||
|
|
||||||
|
Toy_setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
Toy_freeLiteral(name);
|
||||||
|
Toy_freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||||
|
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||||
|
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeLiteral(dict);
|
||||||
|
Toy_freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
#define TOY_OPAQUE_TAG_RANDOM 200
|
||||||
|
|
||||||
|
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
@@ -0,0 +1,511 @@
|
|||||||
|
#include "lib_runner.h"
|
||||||
|
|
||||||
|
#include "toy_memory.h"
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
#include "repl_tools.h"
|
||||||
|
#include "drive_system.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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_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_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_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_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_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_isDeclaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
Toy_freeLiteral(alias);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||||
|
Toy_initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||||
|
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
|
||||||
|
|
||||||
|
Toy_setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
Toy_freeLiteral(name);
|
||||||
|
Toy_freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||||
|
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||||
|
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeLiteral(dict);
|
||||||
|
Toy_freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
#define TOY_OPAQUE_TAG_RUNNER 100
|
||||||
|
|
||||||
|
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
+2161
-32
File diff suppressed because it is too large
Load Diff
+2
-3
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "interpreter.h"
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
|
|
||||||
|
|
||||||
|
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
|
|||||||
@@ -1,411 +0,0 @@
|
|||||||
#include "lib_timer.h"
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
|
|
||||||
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
|
|
||||||
//normallize
|
|
||||||
if (x->tv_usec > 999999) {
|
|
||||||
x->tv_sec += x->tv_usec / 1000000;
|
|
||||||
x->tv_usec %= 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y->tv_usec > 999999) {
|
|
||||||
y->tv_sec += y->tv_usec / 1000000;
|
|
||||||
y->tv_usec %= 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
//calc
|
|
||||||
result->tv_sec = x->tv_sec - y->tv_sec;
|
|
||||||
|
|
||||||
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
|
|
||||||
if (result->tv_sec != 0) { //only works far from 0
|
|
||||||
result->tv_usec += 1000000;
|
|
||||||
result->tv_sec--; // borrow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//god damn it
|
|
||||||
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
|
|
||||||
struct timeval* d = ALLOCATE(struct timeval, 1);
|
|
||||||
|
|
||||||
//I gave up, copied from SO
|
|
||||||
timeval_subtract(d, rhs, lhs);
|
|
||||||
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
//callbacks
|
|
||||||
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 0) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the timeinfo from C
|
|
||||||
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
|
||||||
gettimeofday(timeinfo, NULL);
|
|
||||||
|
|
||||||
//wrap in an opaque literal for Toy
|
|
||||||
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
|
||||||
pushLiteralArray(&interpreter->stack, timeLiteral);
|
|
||||||
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 1) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the timeinfo from C
|
|
||||||
struct timeval timerStop;
|
|
||||||
gettimeofday(&timerStop, NULL);
|
|
||||||
|
|
||||||
//unwrap the opaque literal
|
|
||||||
Literal timeLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal timeLiteralIdn = timeLiteral;
|
|
||||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
|
||||||
freeLiteral(timeLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_OPAQUE(timeLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
|
|
||||||
|
|
||||||
//determine the difference, and wrap it
|
|
||||||
struct timeval* d = diff(timerStart, &timerStop);
|
|
||||||
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
|
||||||
pushLiteralArray(&interpreter->stack, diffLiteral);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
freeLiteral(diffLiteral);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 2) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the args
|
|
||||||
Literal microsecondLiteral = popLiteralArray(arguments);
|
|
||||||
Literal secondLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal secondLiteralIdn = secondLiteral;
|
|
||||||
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
|
|
||||||
freeLiteral(secondLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal microsecondLiteralIdn = microsecondLiteral;
|
|
||||||
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, µsecondLiteral)) {
|
|
||||||
freeLiteral(microsecondLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
|
|
||||||
freeLiteral(secondLiteral);
|
|
||||||
freeLiteral(microsecondLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) {
|
|
||||||
interpreter->errorOutput("Microseconds out of range in createTimer\n");
|
|
||||||
freeLiteral(secondLiteral);
|
|
||||||
freeLiteral(microsecondLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the timeinfo from toy
|
|
||||||
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
|
||||||
timeinfo->tv_sec = AS_INTEGER(secondLiteral);
|
|
||||||
timeinfo->tv_usec = AS_INTEGER(microsecondLiteral);
|
|
||||||
|
|
||||||
//wrap in an opaque literal for Toy
|
|
||||||
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
|
||||||
pushLiteralArray(&interpreter->stack, timeLiteral);
|
|
||||||
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
freeLiteral(secondLiteral);
|
|
||||||
freeLiteral(microsecondLiteral);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 1) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//unwrap the opaque literal
|
|
||||||
Literal timeLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal timeLiteralIdn = timeLiteral;
|
|
||||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
|
||||||
freeLiteral(timeLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_OPAQUE(timeLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
|
||||||
|
|
||||||
//create the result literal
|
|
||||||
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
|
|
||||||
pushLiteralArray(&interpreter->stack, result);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
freeLiteral(result);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 1) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//unwrap the opaque literal
|
|
||||||
Literal timeLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal timeLiteralIdn = timeLiteral;
|
|
||||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
|
||||||
freeLiteral(timeLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_OPAQUE(timeLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
|
||||||
|
|
||||||
//create the result literal
|
|
||||||
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
|
|
||||||
pushLiteralArray(&interpreter->stack, result);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
freeLiteral(result);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 2) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//unwrap the opaque literals
|
|
||||||
Literal rhsLiteral = popLiteralArray(arguments);
|
|
||||||
Literal lhsLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal lhsLiteralIdn = lhsLiteral;
|
|
||||||
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
|
|
||||||
freeLiteral(lhsLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal rhsLiteralIdn = rhsLiteral;
|
|
||||||
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
|
|
||||||
freeLiteral(rhsLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
|
|
||||||
freeLiteral(lhsLiteral);
|
|
||||||
freeLiteral(rhsLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
|
|
||||||
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
|
|
||||||
|
|
||||||
//determine the difference, and wrap it
|
|
||||||
struct timeval* d = diff(lhsTimer, rhsTimer);
|
|
||||||
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
|
||||||
pushLiteralArray(&interpreter->stack, diffLiteral);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
freeLiteral(lhsLiteral);
|
|
||||||
freeLiteral(rhsLiteral);
|
|
||||||
freeLiteral(diffLiteral);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 1) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//unwrap in an opaque literal
|
|
||||||
Literal timeLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal timeLiteralIdn = timeLiteral;
|
|
||||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
|
||||||
freeLiteral(timeLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_OPAQUE(timeLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
|
||||||
|
|
||||||
//create the string literal
|
|
||||||
Literal resultLiteral = TO_NULL_LITERAL;
|
|
||||||
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
|
|
||||||
char buffer[128];
|
|
||||||
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
|
|
||||||
resultLiteral = TO_STRING_LITERAL(createRefStringLength(buffer, strlen(buffer)));
|
|
||||||
}
|
|
||||||
else { //normal case
|
|
||||||
char buffer[128];
|
|
||||||
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
|
|
||||||
resultLiteral = TO_STRING_LITERAL(createRefStringLength(buffer, strlen(buffer)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pushLiteralArray(&interpreter->stack, resultLiteral);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
freeLiteral(resultLiteral);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
|
||||||
//no arguments
|
|
||||||
if (arguments->count != 1) {
|
|
||||||
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//unwrap in an opaque literal
|
|
||||||
Literal timeLiteral = popLiteralArray(arguments);
|
|
||||||
|
|
||||||
Literal timeLiteralIdn = timeLiteral;
|
|
||||||
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
|
||||||
freeLiteral(timeLiteralIdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_OPAQUE(timeLiteral)) {
|
|
||||||
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
|
||||||
|
|
||||||
FREE(struct timeval, timer);
|
|
||||||
|
|
||||||
freeLiteral(timeLiteral);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//call the hook
|
|
||||||
typedef struct Natives {
|
|
||||||
char* name;
|
|
||||||
NativeFn fn;
|
|
||||||
} Natives;
|
|
||||||
|
|
||||||
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
|
|
||||||
//build the natives list
|
|
||||||
Natives natives[] = {
|
|
||||||
{"startTimer", nativeStartTimer},
|
|
||||||
{"_stopTimer", nativeStopTimer},
|
|
||||||
{"createTimer", nativeCreateTimer},
|
|
||||||
{"_getTimerSeconds", nativeGetTimerSeconds},
|
|
||||||
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
|
|
||||||
{"_compareTimer", nativeCompareTimer},
|
|
||||||
{"_timerToString", nativeTimerToString},
|
|
||||||
{"_destroyTimer", nativeDestroyTimer},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
//store the library in an aliased dictionary
|
|
||||||
if (!IS_NULL(alias)) {
|
|
||||||
//make sure the name isn't taken
|
|
||||||
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
|
||||||
interpreter->errorOutput("Can't override an existing variable\n");
|
|
||||||
freeLiteral(alias);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//create the dictionary to load up with functions
|
|
||||||
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
|
||||||
initLiteralDictionary(dictionary);
|
|
||||||
|
|
||||||
//load the dict with functions
|
|
||||||
for (int i = 0; natives[i].name; i++) {
|
|
||||||
Literal name = TO_STRING_LITERAL(createRefString(natives[i].name));
|
|
||||||
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
|
||||||
func.type = LITERAL_FUNCTION_NATIVE;
|
|
||||||
|
|
||||||
setLiteralDictionary(dictionary, name, func);
|
|
||||||
|
|
||||||
freeLiteral(name);
|
|
||||||
freeLiteral(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
//build the type
|
|
||||||
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
|
||||||
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
|
||||||
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
|
||||||
TYPE_PUSH_SUBTYPE(&type, strType);
|
|
||||||
TYPE_PUSH_SUBTYPE(&type, fnType);
|
|
||||||
|
|
||||||
//set scope
|
|
||||||
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
|
||||||
declareScopeVariable(interpreter->scope, alias, type);
|
|
||||||
setScopeVariable(interpreter->scope, alias, dict, false);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
freeLiteral(dict);
|
|
||||||
freeLiteral(type);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//default
|
|
||||||
for (int i = 0; natives[i].name; i++) {
|
|
||||||
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "interpreter.h"
|
|
||||||
|
|
||||||
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
|
|
||||||
|
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
#include "lib_toy_version_info.h"
|
||||||
|
|
||||||
|
#include "toy_memory.h"
|
||||||
|
|
||||||
|
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||||
|
//the info keys
|
||||||
|
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
|
||||||
|
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
|
||||||
|
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
|
||||||
|
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
|
||||||
|
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
|
||||||
|
|
||||||
|
//the info identifiers
|
||||||
|
Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
|
||||||
|
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
|
||||||
|
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
|
||||||
|
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
|
||||||
|
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
|
||||||
|
|
||||||
|
//the info values
|
||||||
|
Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
|
||||||
|
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
|
||||||
|
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
|
||||||
|
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_isDeclaredScopeVariable(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_isDeclaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
|
||||||
|
Toy_isDeclaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
|
||||||
|
Toy_isDeclaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
|
||||||
|
Toy_isDeclaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
|
||||||
|
Toy_isDeclaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
Toy_freeLiteral(alias);
|
||||||
|
|
||||||
|
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,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||||
+1
-1
@@ -2,7 +2,7 @@ CC=gcc
|
|||||||
|
|
||||||
IDIR+=. ../source
|
IDIR+=. ../source
|
||||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||||
LIBS+=-ltoy
|
LIBS+=-ltoy -lm
|
||||||
|
|
||||||
ODIR = obj
|
ODIR = obj
|
||||||
SRC = $(wildcard *.c)
|
SRC = $(wildcard *.c)
|
||||||
|
|||||||
+149
-57
@@ -1,36 +1,46 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
|
#include "drive_system.h"
|
||||||
|
#include "lib_toy_version_info.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
#include "lib_timer.h"
|
#include "lib_random.h"
|
||||||
|
#include "lib_runner.h"
|
||||||
|
#include "lib_math.h"
|
||||||
|
|
||||||
#include "console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
#include "lexer.h"
|
#include "toy.h"
|
||||||
#include "parser.h"
|
|
||||||
#include "compiler.h"
|
|
||||||
#include "interpreter.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void repl() {
|
#define INPUT_BUFFER_SIZE 2048
|
||||||
|
|
||||||
|
void repl(const char* initialInput) {
|
||||||
//repl does it's own thing for now
|
//repl does it's own thing for now
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
const int size = 2048;
|
char input[INPUT_BUFFER_SIZE];
|
||||||
char input[size];
|
memset(input, 0, INPUT_BUFFER_SIZE);
|
||||||
memset(input, 0, size);
|
|
||||||
|
|
||||||
Interpreter interpreter; //persist the interpreter for the scopes
|
Toy_Interpreter interpreter; //persist the interpreter for the scopes
|
||||||
initInterpreter(&interpreter);
|
Toy_initInterpreter(&interpreter);
|
||||||
|
|
||||||
//inject the libs
|
//inject the libs
|
||||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
|
||||||
injectNativeHook(&interpreter, "timer", hookTimer);
|
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||||
|
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||||
|
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||||
|
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
printf("> ");
|
if (!initialInput) {
|
||||||
fgets(input, size, stdin);
|
//handle EOF for exits
|
||||||
|
printf("> ");
|
||||||
|
if (!fgets(input, INPUT_BUFFER_SIZE, stdin)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//escape the repl (length of 5 to accomodate the newline)
|
//escape the repl (length of 5 to accomodate the newline)
|
||||||
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
|
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
|
||||||
@@ -38,104 +48,186 @@ void repl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//setup this iteration
|
//setup this iteration
|
||||||
Lexer lexer;
|
Toy_Lexer lexer;
|
||||||
Parser parser;
|
Toy_Parser parser;
|
||||||
Compiler compiler;
|
Toy_Compiler compiler;
|
||||||
|
|
||||||
initLexer(&lexer, input);
|
Toy_initLexer(&lexer, initialInput ? initialInput : input);
|
||||||
initParser(&parser, &lexer);
|
Toy_private_setComments(&lexer, initialInput != NULL); //BUGFIX: disable comments here
|
||||||
initCompiler(&compiler);
|
Toy_initParser(&parser, &lexer);
|
||||||
|
Toy_initCompiler(&compiler);
|
||||||
|
|
||||||
//run this iteration
|
//run this iteration
|
||||||
ASTNode* node = scanParser(&parser);
|
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||||
while(node != NULL) {
|
while(node != NULL) {
|
||||||
//pack up and restart
|
//pack up and restart
|
||||||
if (node->type == AST_NODE_ERROR) {
|
if (node->type == TOY_AST_NODE_ERROR) {
|
||||||
printf(ERROR "error node detected\n" RESET);
|
if (Toy_commandLine.verbose) {
|
||||||
|
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
|
||||||
|
}
|
||||||
error = true;
|
error = true;
|
||||||
freeASTNode(node);
|
Toy_freeASTNode(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCompiler(&compiler, node);
|
Toy_writeCompiler(&compiler, node);
|
||||||
freeASTNode(node);
|
Toy_freeASTNode(node);
|
||||||
node = scanParser(&parser);
|
node = Toy_scanParser(&parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
//get the bytecode dump
|
//get the bytecode dump
|
||||||
int size = 0;
|
size_t size = 0;
|
||||||
unsigned char* tb = collateCompiler(&compiler, &size);
|
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
//run the bytecode
|
//run the bytecode
|
||||||
runInterpreter(&interpreter, tb, size);
|
Toy_runInterpreter(&interpreter, tb, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
//clean up this iteration
|
//clean up this iteration
|
||||||
freeCompiler(&compiler);
|
Toy_freeCompiler(&compiler);
|
||||||
freeParser(&parser);
|
Toy_freeParser(&parser);
|
||||||
error = false;
|
error = false;
|
||||||
|
|
||||||
|
if (initialInput) {
|
||||||
|
free((void*)initialInput);
|
||||||
|
initialInput = NULL;
|
||||||
|
|
||||||
|
if (interpreter.panic) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
freeInterpreter(&interpreter);
|
Toy_freeInterpreter(&interpreter);
|
||||||
}
|
}
|
||||||
|
|
||||||
//entry point
|
//entry point
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
initCommand(argc, argv);
|
Toy_initCommandLine(argc, argv);
|
||||||
|
|
||||||
//command specific actions
|
//setup the drive system (for filesystem access)
|
||||||
if (command.error) {
|
Toy_initDriveSystem();
|
||||||
usageCommand(argc, argv);
|
Toy_setDrivePath("scripts", "scripts");
|
||||||
|
|
||||||
|
//command line specific actions
|
||||||
|
if (Toy_commandLine.error) {
|
||||||
|
Toy_usageCommandLine(argc, argv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.help) {
|
if (Toy_commandLine.help) {
|
||||||
helpCommand(argc, argv);
|
Toy_helpCommandLine(argc, argv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.version) {
|
if (Toy_commandLine.version) {
|
||||||
copyrightCommand(argc, argv);
|
Toy_copyrightCommandLine(argc, argv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: remove this when the interpreter meets the specification
|
//version
|
||||||
if (command.verbose) {
|
if (Toy_commandLine.verbose) {
|
||||||
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
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
|
//run source file
|
||||||
if (command.sourcefile) {
|
if (Toy_commandLine.sourcefile) {
|
||||||
runSourceFile(command.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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//run from stdin
|
//run from stdin
|
||||||
if (command.source) {
|
if (Toy_commandLine.source) {
|
||||||
runSource(command.source);
|
Toy_runSource(Toy_commandLine.source);
|
||||||
|
|
||||||
|
//lib cleanup
|
||||||
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//compile source file
|
//compile source file
|
||||||
if (command.compilefile && command.outfile) {
|
if (Toy_commandLine.compilefile && Toy_commandLine.outfile) {
|
||||||
|
//only works on toy and tb files
|
||||||
|
const char* c = strrchr(Toy_commandLine.compilefile, '.');
|
||||||
|
if (!c || strcmp(c, ".toy")) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], c);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char* o = strrchr(Toy_commandLine.outfile, '.');
|
||||||
|
if (!o || strcmp(o, ".tb")) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.tb', found '%s')" TOY_CC_RESET, argv[0], o);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//compile and save
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char* source = readFile(command.compilefile, &size);
|
const char* source = (const char*)Toy_readFile(Toy_commandLine.compilefile, &size);
|
||||||
unsigned char* tb = compileString(source, &size);
|
if (!source) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const unsigned char* tb = Toy_compileString(source, &size);
|
||||||
if (!tb) {
|
if (!tb) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
writeFile(command.outfile, tb, size);
|
Toy_writeFile(Toy_commandLine.outfile, tb, size);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//run binary
|
//run binary
|
||||||
if (command.binaryfile) {
|
if (Toy_commandLine.binaryfile) {
|
||||||
runBinaryFile(command.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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
repl();
|
const char* initialSource = NULL;
|
||||||
|
if (Toy_commandLine.initialfile) {
|
||||||
|
//only works on toy files
|
||||||
|
const char* s = strrchr(Toy_commandLine.initialfile, '.');
|
||||||
|
if (!s || strcmp(s, ".toy")) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
initialSource = (const char*)Toy_readFile(Toy_commandLine.initialfile, &size);
|
||||||
|
}
|
||||||
|
|
||||||
|
repl(initialSource);
|
||||||
|
|
||||||
|
//lib cleanup
|
||||||
|
Toy_freeDriveSystem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+125
-59
@@ -1,44 +1,47 @@
|
|||||||
#include "repl_tools.h"
|
#include "repl_tools.h"
|
||||||
|
#include "lib_toy_version_info.h"
|
||||||
#include "lib_standard.h"
|
#include "lib_standard.h"
|
||||||
#include "lib_timer.h"
|
#include "lib_random.h"
|
||||||
|
#include "lib_runner.h"
|
||||||
|
#include "lib_math.h"
|
||||||
|
|
||||||
#include "console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
#include "lexer.h"
|
#include "toy_lexer.h"
|
||||||
#include "parser.h"
|
#include "toy_parser.h"
|
||||||
#include "compiler.h"
|
#include "toy_compiler.h"
|
||||||
#include "interpreter.h"
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
//IO functions
|
//IO functions
|
||||||
char* readFile(char* path, size_t* fileSize) {
|
const unsigned char* Toy_readFile(const char* path, size_t* fileSize) {
|
||||||
FILE* file = fopen(path, "rb");
|
FILE* file = fopen(path, "rb");
|
||||||
|
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||||
exit(-1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(file, 0L, SEEK_END);
|
fseek(file, 0L, SEEK_END);
|
||||||
*fileSize = ftell(file);
|
*fileSize = ftell(file);
|
||||||
rewind(file);
|
rewind(file);
|
||||||
|
|
||||||
char* buffer = (char*)malloc(*fileSize + 1);
|
unsigned char* buffer = (unsigned char*)malloc(*fileSize + 1);
|
||||||
|
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
||||||
exit(-1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
size_t bytesRead = fread(buffer, sizeof(unsigned char), *fileSize, file);
|
||||||
|
|
||||||
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||||
|
|
||||||
if (bytesRead < *fileSize) {
|
if (bytesRead < *fileSize) {
|
||||||
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
|
||||||
exit(-1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
@@ -46,98 +49,161 @@ char* readFile(char* path, size_t* fileSize) {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeFile(char* path, unsigned char* bytes, size_t size) {
|
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
|
||||||
FILE* file = fopen(path, "wb");
|
FILE* file = fopen(path, "wb");
|
||||||
|
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int written = fwrite(bytes, size, 1, file);
|
size_t written = fwrite(bytes, size, 1, file);
|
||||||
|
|
||||||
if (written != 1) {
|
if (written != 1) {
|
||||||
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
|
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//repl functions
|
//repl functions
|
||||||
unsigned char* compileString(char* source, size_t* size) {
|
const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
||||||
Lexer lexer;
|
Toy_Lexer lexer;
|
||||||
Parser parser;
|
Toy_Parser parser;
|
||||||
Compiler compiler;
|
Toy_Compiler compiler;
|
||||||
|
|
||||||
initLexer(&lexer, source);
|
Toy_initLexer(&lexer, source);
|
||||||
initParser(&parser, &lexer);
|
Toy_initParser(&parser, &lexer);
|
||||||
initCompiler(&compiler);
|
Toy_initCompiler(&compiler);
|
||||||
|
|
||||||
//run the parser until the end of the source
|
//step 1 - run the parser until the end of the source
|
||||||
ASTNode* node = scanParser(&parser);
|
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||||
while(node != NULL) {
|
while(node != NULL) {
|
||||||
//pack up and leave
|
//on error, pack up and leave
|
||||||
if (node->type == AST_NODE_ERROR) {
|
if (node->type == TOY_AST_NODE_ERROR) {
|
||||||
printf(ERROR "error node detected\n" RESET);
|
Toy_freeASTNode(node);
|
||||||
freeASTNode(node);
|
Toy_freeCompiler(&compiler);
|
||||||
freeCompiler(&compiler);
|
Toy_freeParser(&parser);
|
||||||
freeParser(&parser);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCompiler(&compiler, node);
|
Toy_writeCompiler(&compiler, node);
|
||||||
freeASTNode(node);
|
Toy_freeASTNode(node);
|
||||||
node = scanParser(&parser);
|
node = Toy_scanParser(&parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the bytecode dump
|
//step 2 - get the bytecode dump
|
||||||
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
|
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
||||||
|
|
||||||
//cleanup
|
//cleanup
|
||||||
freeCompiler(&compiler);
|
Toy_freeCompiler(&compiler);
|
||||||
freeParser(&parser);
|
Toy_freeParser(&parser);
|
||||||
//no lexer to clean up
|
//no lexer to clean up
|
||||||
|
|
||||||
//finally
|
//finally
|
||||||
return tb;
|
return tb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void runBinary(unsigned char* tb, size_t size) {
|
void Toy_runBinary(const unsigned char* tb, size_t size) {
|
||||||
Interpreter interpreter;
|
Toy_Interpreter interpreter;
|
||||||
initInterpreter(&interpreter);
|
Toy_initInterpreter(&interpreter);
|
||||||
|
|
||||||
//inject the libs
|
//inject the libs
|
||||||
injectNativeHook(&interpreter, "standard", hookStandard);
|
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
|
||||||
injectNativeHook(&interpreter, "timer", hookTimer);
|
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||||
|
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
||||||
|
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||||
|
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
|
||||||
|
|
||||||
runInterpreter(&interpreter, tb, size);
|
Toy_runInterpreter(&interpreter, tb, (int)size);
|
||||||
freeInterpreter(&interpreter);
|
Toy_freeInterpreter(&interpreter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runBinaryFile(char* fname) {
|
void Toy_runBinaryFile(const char* fname) {
|
||||||
size_t size = 0; //not used
|
size_t size = 0; //not used
|
||||||
unsigned char* tb = (unsigned char*)readFile(fname, &size);
|
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||||
if (!tb) {
|
if (!tb) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runBinary(tb, size);
|
Toy_runBinary(tb, size);
|
||||||
//interpreter takes ownership of the binary data
|
//interpreter takes ownership of the binary data
|
||||||
}
|
}
|
||||||
|
|
||||||
void runSource(char* source) {
|
void Toy_runSource(const char* source) {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
unsigned char* tb = compileString(source, &size);
|
const unsigned char* tb = Toy_compileString(source, &size);
|
||||||
if (!tb) {
|
if (!tb) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runBinary(tb, size);
|
Toy_runBinary(tb, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runSourceFile(char* fname) {
|
void Toy_runSourceFile(const char* fname) {
|
||||||
size_t size = 0; //not used
|
size_t size = 0; //not used
|
||||||
char* source = readFile(fname, &size);
|
const char* source = (const char*)Toy_readFile(fname, &size);
|
||||||
runSource(source);
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toy_runSource(source);
|
||||||
free((void*)source);
|
free((void*)source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//utils for debugging the header
|
||||||
|
static unsigned char readByte(const unsigned char* tb, int* count) {
|
||||||
|
unsigned char ret = *(unsigned char*)(tb + *count);
|
||||||
|
*count += 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* readString(const unsigned char* tb, int* count) {
|
||||||
|
const unsigned char* ret = tb + *count;
|
||||||
|
*count += (int)strlen((char*)ret) + 1; //+1 for null character
|
||||||
|
return (const char*)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_parseBinaryFileHeader(const char* fname) {
|
||||||
|
size_t size = 0; //not used
|
||||||
|
const unsigned char* tb = Toy_readFile(fname, &size);
|
||||||
|
if (!tb || size < 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
//header section
|
||||||
|
const unsigned char major = readByte(tb, &count);
|
||||||
|
const unsigned char minor = readByte(tb, &count);
|
||||||
|
const unsigned char patch = readByte(tb, &count);
|
||||||
|
|
||||||
|
const char* build = readString(tb, &count);
|
||||||
|
|
||||||
|
printf("Toy Programming Language Interpreter Version %d.%d.%d (interpreter built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||||
|
|
||||||
|
printf("Toy Programming Language Bytecode Version ");
|
||||||
|
|
||||||
|
//print the output
|
||||||
|
if (major == TOY_VERSION_MAJOR && minor == TOY_VERSION_MINOR && patch == TOY_VERSION_PATCH) {
|
||||||
|
printf("%d.%d.%d", major, minor, patch);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%d.%d.%d" TOY_CC_RESET, major, minor, patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" (interpreter built on ");
|
||||||
|
|
||||||
|
if (strncmp(build, TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD)) == 0) {
|
||||||
|
printf("%s", build);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK "%s" TOY_CC_RESET, build);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(")\n");
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
free((void*)tb);
|
||||||
|
}
|
||||||
+77
-7
@@ -1,14 +1,84 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# repl_tools.h
|
||||||
|
|
||||||
|
This header provides a number of tools for compiling and running Toy, and is used primarily by the repl. However, it can also be modified and used by any host program with a little effort.
|
||||||
|
|
||||||
|
This is not a core part of Toy or a library, and as such `repl_tools.h` and `repl_tools.c` can both be found in the `repl/` folder.
|
||||||
|
!*/
|
||||||
|
|
||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
char* readFile(char* path, size_t* fileSize);
|
/*!
|
||||||
void writeFile(char* path, unsigned char* bytes, size_t size);
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
unsigned char* compileString(char* source, size_t* size);
|
/*!
|
||||||
|
### const char* Toy_readFile(const char* path, size_t* fileSize)
|
||||||
|
|
||||||
void runBinary(unsigned char* tb, size_t size);
|
This function reads in a file, and returns it as a constant buffer. It also sets the variable pointed to by `fileSize` to the size of the given buffer.
|
||||||
void runBinaryFile(char* fname);
|
|
||||||
void runSource(char* source);
|
|
||||||
void runSourceFile(char* fname);
|
|
||||||
|
|
||||||
|
On error, this function returns `NULL`.
|
||||||
|
!*/
|
||||||
|
const unsigned char* Toy_readFile(const char* path, size_t* fileSize);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size)
|
||||||
|
|
||||||
|
This function writes the buffer pointed to by `bytes` to a file specified by `path`. The buffer's size should be specified by `size`.
|
||||||
|
|
||||||
|
On error, this function returns a non-zero value.
|
||||||
|
!*/
|
||||||
|
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### const unsigned char* Toy_compileString(const char* source, size_t* size)
|
||||||
|
|
||||||
|
This function takes a cstring of Toy source code, and returns a compiled buffer based on that source code. The variable pointed to by `size` is set to the size of the bytecode.
|
||||||
|
|
||||||
|
On error, this function returns `NULL`.
|
||||||
|
!*/
|
||||||
|
const unsigned char* Toy_compileString(const char* source, size_t* size);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_runBinary(const unsigned char* tb, size_t size)
|
||||||
|
|
||||||
|
This function takes a bytecode array of `size` size, and executes it. The libraries available to the code are currently:
|
||||||
|
|
||||||
|
* lib_toy_version_info
|
||||||
|
* lib_standard
|
||||||
|
* lib_random
|
||||||
|
* lib_runner
|
||||||
|
!*/
|
||||||
|
void Toy_runBinary(const unsigned char* tb, size_t size);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_runBinaryFile(const char* fname)
|
||||||
|
|
||||||
|
This function loads in the binary file specified by `fname`, and passes it to `Toy_runBinary()`.
|
||||||
|
!*/
|
||||||
|
void Toy_runBinaryFile(const char* fname);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_runSource(const char* source)
|
||||||
|
|
||||||
|
This function compiles the source with `Toy_compileString()`, and passes it to `Toy_runBinary()`.
|
||||||
|
!*/
|
||||||
|
void Toy_runSource(const char* source);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_runSourceFile(const char* fname)
|
||||||
|
|
||||||
|
This function loads in the file specified by `fname`, compiles it, and passes it to `Toy_runBinary()`.
|
||||||
|
!*/
|
||||||
|
void Toy_runSourceFile(const char* fname);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_parseBinaryFileHeader(const char* fname)
|
||||||
|
|
||||||
|
This function parses the header information stored within the bytecode file `fname`.
|
||||||
|
|
||||||
|
This is only used for debugging and validation purposes.
|
||||||
|
!*/
|
||||||
|
void Toy_parseBinaryFileHeader(const char* fname);
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
//single line comment
|
|
||||||
|
|
||||||
/*
|
|
||||||
multi line comment
|
|
||||||
*/
|
|
||||||
|
|
||||||
//test primitive literals
|
|
||||||
print "hello world";
|
|
||||||
print null;
|
|
||||||
print true;
|
|
||||||
print false;
|
|
||||||
print 42;
|
|
||||||
print 3.14;
|
|
||||||
print -69;
|
|
||||||
print -4.20;
|
|
||||||
print 2 + (3 * 3);
|
|
||||||
|
|
||||||
//test operators (integers)
|
|
||||||
print 1 + 1;
|
|
||||||
print 1 - 1;
|
|
||||||
print 2 * 2;
|
|
||||||
print 1 / 2;
|
|
||||||
print 4 % 2;
|
|
||||||
|
|
||||||
//test operators (floats)
|
|
||||||
print 1.0 + 1.0;
|
|
||||||
print 1.0 - 1.0;
|
|
||||||
print 2.0 * 2.0;
|
|
||||||
print 1.0 / 2.0;
|
|
||||||
|
|
||||||
//test scopes
|
|
||||||
{
|
|
||||||
print "This statement is within a scope.";
|
|
||||||
{
|
|
||||||
print "This is a deeper scope.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print "Back to the outer scope.";
|
|
||||||
|
|
||||||
//test scope will delegate to higher scope
|
|
||||||
var a = 1;
|
|
||||||
{
|
|
||||||
a = 2;
|
|
||||||
print a;
|
|
||||||
}
|
|
||||||
print a;
|
|
||||||
|
|
||||||
//test scope will shadow higher scope on redefine
|
|
||||||
var b: int = 3;
|
|
||||||
{
|
|
||||||
var b = 4;
|
|
||||||
print b;
|
|
||||||
}
|
|
||||||
print b;
|
|
||||||
|
|
||||||
//test compounds, repeatedly
|
|
||||||
print [1, 2, 3];
|
|
||||||
print [4, 5];
|
|
||||||
print ["key":"value"];
|
|
||||||
print [1, 2, 3];
|
|
||||||
print [4, 5];
|
|
||||||
print ["key":"value"];
|
|
||||||
|
|
||||||
//test empties
|
|
||||||
print [];
|
|
||||||
print [:];
|
|
||||||
|
|
||||||
//test nested compounds
|
|
||||||
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
|
||||||
|
|
||||||
//var declarations
|
|
||||||
var x = 31;
|
|
||||||
var y : int = 42;
|
|
||||||
var arr : [int] = [1, 2, 3, 42];
|
|
||||||
var dict : [string:int] = ["hello": 1, "world":2];
|
|
||||||
|
|
||||||
//printing expressions
|
|
||||||
print x;
|
|
||||||
print x + y;
|
|
||||||
print arr;
|
|
||||||
print dict;
|
|
||||||
|
|
||||||
//test asserts at the end of the file
|
|
||||||
assert x, "This won't be seen";
|
|
||||||
assert true, "This won't be seen";
|
|
||||||
assert false, "This is a failed assert, and will end execution";
|
|
||||||
|
|
||||||
print "This will not be printed because of the above assert";
|
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//memoize the fib function
|
||||||
|
var memo: [int : int] = [:];
|
||||||
|
|
||||||
|
fn fib(n : int) {
|
||||||
|
if (n < 2) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = memo[n];
|
||||||
|
if (result == null) {
|
||||||
|
result = fib(n-1) + fib(n-2);
|
||||||
|
memo[n] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 40; i++) {
|
||||||
|
var res = fib(i);
|
||||||
|
print string i + ": " + string res;
|
||||||
|
}
|
||||||
+4
-6
@@ -1,12 +1,10 @@
|
|||||||
|
//WARNING: please think twice before using this in a test
|
||||||
fn fib(n : int) {
|
fn fib(n : int) {
|
||||||
if (n < 2) {
|
if (n < 2) return n;
|
||||||
return n;
|
return fib(n-1) + fib(n-2);
|
||||||
}
|
|
||||||
|
|
||||||
return fib(n-1) + fib(n-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 20; i++) {
|
for (var i = 0; i <= 35; i++) {
|
||||||
var res = fib(i);
|
var res = fib(i);
|
||||||
print string i + ": " + string res;
|
print string i + ": " + string res;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
How to run this program:
|
||||||
|
|
||||||
|
toyrepl -n -t scripts/level.toy
|
||||||
|
|
||||||
|
How to move around:
|
||||||
|
|
||||||
|
move(up);
|
||||||
|
move(down);
|
||||||
|
move(left);
|
||||||
|
move(right);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
//constants
|
||||||
|
var WIDTH: int const = 12;
|
||||||
|
var HEIGHT: int const = 12;
|
||||||
|
|
||||||
|
//WIDTH * HEIGHT in size
|
||||||
|
var tiles: [[int]] const = [
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||||
|
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||||
|
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
|
||||||
|
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] //BUG: map is twisted along this diagonal
|
||||||
|
];
|
||||||
|
|
||||||
|
var tileset: [int: string] const = [
|
||||||
|
0: " ",
|
||||||
|
1: "X "
|
||||||
|
];
|
||||||
|
|
||||||
|
//variables
|
||||||
|
var posX: int = 4;
|
||||||
|
var posY: int = 4;
|
||||||
|
|
||||||
|
//functions
|
||||||
|
fn draw() {
|
||||||
|
for (var j: int = 0; j < HEIGHT; j++) {
|
||||||
|
for (var i: int = 0; i < WIDTH; i++) {
|
||||||
|
//draw the player pos
|
||||||
|
if (i == posX && j == posY) {
|
||||||
|
print "O ";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print tileset[ tiles[i][j] ];
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moveRelative(xrel: int, yrel: int) {
|
||||||
|
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1 || (xrel != 0 && yrel != 0)) {
|
||||||
|
print "too fast!\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tiles[posX + xrel][posY + yrel] > 0) {
|
||||||
|
print "Can't move that way\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
posX += xrel;
|
||||||
|
posY += yrel;
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
//wrap for easy use
|
||||||
|
var up: [int] const = [0, -1];
|
||||||
|
var down: [int] const = [0, 1];
|
||||||
|
var left: [int] const = [-1, 0];
|
||||||
|
var right: [int] const = [1, 0];
|
||||||
|
|
||||||
|
fn move(dir: [int] const) {
|
||||||
|
return moveRelative(dir[0], dir[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//initial display
|
||||||
|
move([0, 0]);
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Since this is a pseudo-random generator, and there's no internal state to the algorithm other
|
||||||
|
than the generator opaque, there needs to be a "call counter" (current depth) to shuffle the
|
||||||
|
initial seeds, otherwise generators created from other generators will resemble their parents,
|
||||||
|
but one call greater.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import standard;
|
||||||
|
import random;
|
||||||
|
|
||||||
|
var DEPTH: int const = 20;
|
||||||
|
var levels = [];
|
||||||
|
|
||||||
|
//generate the level seeds
|
||||||
|
var generator: opaque = createRandomGenerator(clock().hash());
|
||||||
|
|
||||||
|
for (var i: int = 0; i < DEPTH; i++) {
|
||||||
|
levels.push(generator.generateRandomNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.freeRandomGenerator();
|
||||||
|
|
||||||
|
//generate "levels" of a roguelike
|
||||||
|
for (var i = 0; i < DEPTH; i++) {
|
||||||
|
var rng: opaque = createRandomGenerator(levels[i] + i);
|
||||||
|
|
||||||
|
print "---";
|
||||||
|
print levels[i];
|
||||||
|
print rng.generateRandomNumber();
|
||||||
|
print rng.generateRandomNumber();
|
||||||
|
print rng.generateRandomNumber();
|
||||||
|
|
||||||
|
rng.freeRandomGenerator();
|
||||||
|
}
|
||||||
+40
-57
@@ -1,66 +1,49 @@
|
|||||||
var size: int const = 100;
|
//number of iterations
|
||||||
|
var SIZE: int const = 100;
|
||||||
|
|
||||||
var prev = [];
|
//lookup table
|
||||||
for (var i = 0; i < size; i++) {
|
var lookup = [
|
||||||
prev.push(false);
|
"*": [
|
||||||
|
"*": [
|
||||||
|
"*": " ",
|
||||||
|
" ": "*"
|
||||||
|
],
|
||||||
|
" ": [
|
||||||
|
"*": "*",
|
||||||
|
" ": " "
|
||||||
|
]
|
||||||
|
], " ": [
|
||||||
|
"*": [
|
||||||
|
"*": "*",
|
||||||
|
" ": "*"
|
||||||
|
],
|
||||||
|
" ": [
|
||||||
|
"*": "*",
|
||||||
|
" ": " "
|
||||||
|
]
|
||||||
|
]];
|
||||||
|
|
||||||
|
//initial line to build from
|
||||||
|
var prev: string = "";
|
||||||
|
for (var i = 0; i < SIZE -1; i++) {
|
||||||
|
prev += " ";
|
||||||
}
|
}
|
||||||
|
prev += "*"; //initial
|
||||||
|
print prev;
|
||||||
|
|
||||||
prev.set(size - 1, true);
|
//run
|
||||||
|
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
||||||
|
//left
|
||||||
|
var output = (lookup[" "][prev[0]][prev[1]]);
|
||||||
|
|
||||||
|
//middle
|
||||||
fn calc(p, i) {
|
for (var i = 1; i < SIZE-1; i++) {
|
||||||
if (p[i-1] && p[i] && p[i+1]) {
|
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p[i-1] && p[i] && !p[i+1]) {
|
//right
|
||||||
return true;
|
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||||
}
|
|
||||||
|
|
||||||
if (p[i-1] && !p[i] && p[i+1]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p[i-1] && !p[i] && !p[i+1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p[i-1] && p[i] && p[i+1]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p[i-1] && p[i] && !p[i+1]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p[i-1] && !p[i] && p[i+1]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p[i-1] && !p[i] && !p[i+1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (var iteration = 0; iteration < 100; iteration++) {
|
|
||||||
var line = [false];
|
|
||||||
for (var i = 1; i < size-1; i++) {
|
|
||||||
line.push(calc(prev, i));
|
|
||||||
}
|
|
||||||
line.push(false);
|
|
||||||
|
|
||||||
var output = "";
|
|
||||||
for (var i = 0; i < line.length(); i++) {
|
|
||||||
if (line[i]) {
|
|
||||||
output += "*";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
output += " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print output;
|
print output;
|
||||||
prev = line;
|
prev = output;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import timer;
|
|
||||||
|
|
||||||
var a = createTimer(1, 0);
|
|
||||||
var b = createTimer(2, 0);
|
|
||||||
|
|
||||||
print a.compareTimer(b).timerToString();
|
|
||||||
print b.compareTimer(a).timerToString();
|
|
||||||
|
|
||||||
var c = createTimer(0, 1);
|
|
||||||
var d = createTimer(0, 2);
|
|
||||||
|
|
||||||
print c.compareTimer(d).timerToString();
|
|
||||||
print d.compareTimer(c).timerToString();
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fn f() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g() {
|
||||||
|
fn i() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h() {
|
||||||
|
//
|
||||||
|
}
|
||||||
@@ -1,383 +0,0 @@
|
|||||||
#include "ast_node.h"
|
|
||||||
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
void freeASTNodeCustom(ASTNode* node, bool freeSelf) {
|
|
||||||
//don't free a NULL node
|
|
||||||
if (node == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(node->type) {
|
|
||||||
case AST_NODE_ERROR:
|
|
||||||
//NO-OP
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_LITERAL:
|
|
||||||
freeLiteral(node->atomic.literal);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_UNARY:
|
|
||||||
freeASTNode(node->unary.child);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_BINARY:
|
|
||||||
freeASTNode(node->binary.left);
|
|
||||||
freeASTNode(node->binary.right);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_GROUPING:
|
|
||||||
freeASTNode(node->grouping.child);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_BLOCK:
|
|
||||||
for (int i = 0; i < node->block.count; i++) {
|
|
||||||
freeASTNodeCustom(node->block.nodes + i, false);
|
|
||||||
}
|
|
||||||
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_COMPOUND:
|
|
||||||
for (int i = 0; i < node->compound.count; i++) {
|
|
||||||
freeASTNodeCustom(node->compound.nodes + i, false);
|
|
||||||
}
|
|
||||||
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_PAIR:
|
|
||||||
freeASTNode(node->pair.left);
|
|
||||||
freeASTNode(node->pair.right);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_INDEX:
|
|
||||||
freeASTNode(node->index.first);
|
|
||||||
freeASTNode(node->index.second);
|
|
||||||
freeASTNode(node->index.third);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_VAR_DECL:
|
|
||||||
freeLiteral(node->varDecl.identifier);
|
|
||||||
freeLiteral(node->varDecl.typeLiteral);
|
|
||||||
freeASTNode(node->varDecl.expression);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_FN_COLLECTION:
|
|
||||||
for (int i = 0; i < node->fnCollection.count; i++) {
|
|
||||||
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
|
||||||
}
|
|
||||||
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_FN_DECL:
|
|
||||||
freeLiteral(node->fnDecl.identifier);
|
|
||||||
freeASTNode(node->fnDecl.arguments);
|
|
||||||
freeASTNode(node->fnDecl.returns);
|
|
||||||
freeASTNode(node->fnDecl.block);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_FN_CALL:
|
|
||||||
freeASTNode(node->fnCall.arguments);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_FN_RETURN:
|
|
||||||
freeASTNode(node->returns.returns);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_IF:
|
|
||||||
freeASTNode(node->pathIf.condition);
|
|
||||||
freeASTNode(node->pathIf.thenPath);
|
|
||||||
freeASTNode(node->pathIf.elsePath);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_WHILE:
|
|
||||||
freeASTNode(node->pathWhile.condition);
|
|
||||||
freeASTNode(node->pathWhile.thenPath);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_FOR:
|
|
||||||
freeASTNode(node->pathFor.preClause);
|
|
||||||
freeASTNode(node->pathFor.postClause);
|
|
||||||
freeASTNode(node->pathFor.condition);
|
|
||||||
freeASTNode(node->pathFor.thenPath);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_BREAK:
|
|
||||||
//NO-OP
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_CONTINUE:
|
|
||||||
//NO-OP
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_PREFIX_INCREMENT:
|
|
||||||
freeLiteral(node->prefixIncrement.identifier);
|
|
||||||
break;
|
|
||||||
case AST_NODE_PREFIX_DECREMENT:
|
|
||||||
freeLiteral(node->prefixDecrement.identifier);
|
|
||||||
break;
|
|
||||||
case AST_NODE_POSTFIX_INCREMENT:
|
|
||||||
freeLiteral(node->postfixIncrement.identifier);
|
|
||||||
break;
|
|
||||||
case AST_NODE_POSTFIX_DECREMENT:
|
|
||||||
freeLiteral(node->postfixDecrement.identifier);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_IMPORT:
|
|
||||||
freeLiteral(node->import.identifier);
|
|
||||||
freeLiteral(node->import.alias);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AST_NODE_EXPORT:
|
|
||||||
freeLiteral(node->export.identifier);
|
|
||||||
freeLiteral(node->export.alias);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (freeSelf) {
|
|
||||||
FREE(ASTNode, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeASTNode(ASTNode* node) {
|
|
||||||
freeASTNodeCustom(node, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//various emitters
|
|
||||||
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
|
|
||||||
//allocate a new node
|
|
||||||
*nodeHandle = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
(*nodeHandle)->type = AST_NODE_LITERAL;
|
|
||||||
(*nodeHandle)->atomic.literal = copyLiteral(literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
|
|
||||||
//allocate a new node
|
|
||||||
*nodeHandle = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
(*nodeHandle)->type = AST_NODE_UNARY;
|
|
||||||
(*nodeHandle)->unary.opcode = opcode;
|
|
||||||
(*nodeHandle)->unary.child = child;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_BINARY;
|
|
||||||
tmp->binary.opcode = opcode;
|
|
||||||
tmp->binary.left = *nodeHandle;
|
|
||||||
tmp->binary.right = rhs;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeGrouping(ASTNode** nodeHandle) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_GROUPING;
|
|
||||||
tmp->grouping.child = *nodeHandle;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeBlock(ASTNode** nodeHandle) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_BLOCK;
|
|
||||||
tmp->block.nodes = NULL; //NOTE: appended by the parser
|
|
||||||
tmp->block.capacity = 0;
|
|
||||||
tmp->block.count = 0;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_COMPOUND;
|
|
||||||
tmp->compound.literalType = literalType;
|
|
||||||
tmp->compound.nodes = NULL;
|
|
||||||
tmp->compound.capacity = 0;
|
|
||||||
tmp->compound.count = 0;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right) {
|
|
||||||
//set - assume the node has already been allocated
|
|
||||||
node->type = AST_NODE_PAIR;
|
|
||||||
node->pair.left = left;
|
|
||||||
node->pair.right = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_INDEX;
|
|
||||||
tmp->index.first = first;
|
|
||||||
tmp->index.second = second;
|
|
||||||
tmp->index.third = third;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_VAR_DECL;
|
|
||||||
tmp->varDecl.identifier = identifier;
|
|
||||||
tmp->varDecl.typeLiteral = typeLiteral;
|
|
||||||
tmp->varDecl.expression = expression;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_FN_COLLECTION;
|
|
||||||
tmp->fnCollection.nodes = NULL;
|
|
||||||
tmp->fnCollection.capacity = 0;
|
|
||||||
tmp->fnCollection.count = 0;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_FN_DECL;
|
|
||||||
tmp->fnDecl.identifier = identifier;
|
|
||||||
tmp->fnDecl.arguments = arguments;
|
|
||||||
tmp->fnDecl.returns = returns;
|
|
||||||
tmp->fnDecl.block = block;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeFnCall(ASTNode** nodeHandle, ASTNode* arguments) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_FN_CALL;
|
|
||||||
tmp->fnCall.arguments = arguments;
|
|
||||||
tmp->fnCall.argumentCount = arguments->fnCollection.count;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeFnReturn(ASTNode** nodeHandle, ASTNode* returns) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_FN_RETURN;
|
|
||||||
tmp->returns.returns = returns;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeIf(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_IF;
|
|
||||||
tmp->pathIf.condition = condition;
|
|
||||||
tmp->pathIf.thenPath = thenPath;
|
|
||||||
tmp->pathIf.elsePath = elsePath;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeWhile(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_WHILE;
|
|
||||||
tmp->pathWhile.condition = condition;
|
|
||||||
tmp->pathWhile.thenPath = thenPath;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeFor(ASTNode** nodeHandle, ASTNode* preClause, ASTNode* condition, ASTNode* postClause, ASTNode* thenPath) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_FOR;
|
|
||||||
tmp->pathFor.preClause = preClause;
|
|
||||||
tmp->pathFor.condition = condition;
|
|
||||||
tmp->pathFor.postClause = postClause;
|
|
||||||
tmp->pathFor.thenPath = thenPath;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeBreak(ASTNode** nodeHandle) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_BREAK;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeContinue(ASTNode** nodeHandle) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_CONTINUE;
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_PREFIX_INCREMENT;
|
|
||||||
tmp->prefixIncrement.identifier = copyLiteral(identifier);
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodePrefixDecrement(ASTNode** nodeHandle, Literal identifier) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_PREFIX_DECREMENT;
|
|
||||||
tmp->prefixDecrement.identifier = copyLiteral(identifier);
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_POSTFIX_INCREMENT;
|
|
||||||
tmp->postfixIncrement.identifier = copyLiteral(identifier);
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodePostfixDecrement(ASTNode** nodeHandle, Literal identifier) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_POSTFIX_DECREMENT;
|
|
||||||
tmp->postfixDecrement.identifier = copyLiteral(identifier);
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_IMPORT;
|
|
||||||
tmp->import.identifier = copyLiteral(identifier);
|
|
||||||
tmp->import.alias = copyLiteral(alias);
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emitASTNodeExport(ASTNode** nodeHandle, Literal identifier, Literal alias) {
|
|
||||||
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
|
||||||
|
|
||||||
tmp->type = AST_NODE_EXPORT;
|
|
||||||
tmp->export.identifier = copyLiteral(identifier);
|
|
||||||
tmp->export.alias = copyLiteral(alias);
|
|
||||||
|
|
||||||
*nodeHandle = tmp;
|
|
||||||
}
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
#include "literal.h"
|
|
||||||
#include "opcodes.h"
|
|
||||||
#include "token_types.h"
|
|
||||||
|
|
||||||
//nodes are the intermediaries between parsers and compilers
|
|
||||||
typedef union _node ASTNode;
|
|
||||||
|
|
||||||
typedef enum ASTNodeType {
|
|
||||||
AST_NODE_ERROR,
|
|
||||||
AST_NODE_LITERAL, //a simple value
|
|
||||||
AST_NODE_UNARY, //one child + opcode
|
|
||||||
AST_NODE_BINARY, //two children, left and right + opcode
|
|
||||||
AST_NODE_GROUPING, //one child
|
|
||||||
AST_NODE_BLOCK, //contains a sub-node array
|
|
||||||
AST_NODE_COMPOUND, //contains a sub-node array
|
|
||||||
AST_NODE_PAIR, //contains a left and right
|
|
||||||
AST_NODE_INDEX, //index a variable
|
|
||||||
AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
|
|
||||||
AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
|
|
||||||
AST_NODE_FN_COLLECTION, //parts of a function
|
|
||||||
AST_NODE_FN_CALL, //call a function
|
|
||||||
AST_NODE_FN_RETURN, //for control flow
|
|
||||||
AST_NODE_IF, //for control flow
|
|
||||||
AST_NODE_WHILE, //for control flow
|
|
||||||
AST_NODE_FOR, //for control flow
|
|
||||||
AST_NODE_BREAK, //for control flow
|
|
||||||
AST_NODE_CONTINUE, //for control flow
|
|
||||||
AST_NODE_PREFIX_INCREMENT, //increment a variable
|
|
||||||
AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
|
||||||
AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
|
||||||
AST_NODE_POSTFIX_DECREMENT, //decrement a variable
|
|
||||||
AST_NODE_IMPORT, //import a variable
|
|
||||||
AST_NODE_EXPORT, //export a variable
|
|
||||||
} ASTNodeType;
|
|
||||||
|
|
||||||
//literals
|
|
||||||
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
|
|
||||||
|
|
||||||
typedef struct NodeLiteral {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal literal;
|
|
||||||
} NodeLiteral;
|
|
||||||
|
|
||||||
//unary operator
|
|
||||||
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
|
|
||||||
|
|
||||||
typedef struct NodeUnary {
|
|
||||||
ASTNodeType type;
|
|
||||||
Opcode opcode;
|
|
||||||
ASTNode* child;
|
|
||||||
} NodeUnary;
|
|
||||||
|
|
||||||
//binary operator
|
|
||||||
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
|
|
||||||
|
|
||||||
typedef struct NodeBinary {
|
|
||||||
ASTNodeType type;
|
|
||||||
Opcode opcode;
|
|
||||||
ASTNode* left;
|
|
||||||
ASTNode* right;
|
|
||||||
} NodeBinary;
|
|
||||||
|
|
||||||
//grouping of other AST nodes
|
|
||||||
void emitASTNodeGrouping(ASTNode** nodeHandle);
|
|
||||||
|
|
||||||
typedef struct NodeGrouping {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* child;
|
|
||||||
} NodeGrouping;
|
|
||||||
|
|
||||||
//block of statement nodes
|
|
||||||
void emitASTNodeBlock(ASTNode** nodeHandle);
|
|
||||||
|
|
||||||
typedef struct NodeBlock {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* nodes;
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
} NodeBlock;
|
|
||||||
|
|
||||||
//compound literals (array, dictionary)
|
|
||||||
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
|
|
||||||
|
|
||||||
typedef struct NodeCompound {
|
|
||||||
ASTNodeType type;
|
|
||||||
LiteralType literalType;
|
|
||||||
ASTNode* nodes;
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
} NodeCompound;
|
|
||||||
|
|
||||||
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right); //NOTE: this is a set function, not an emit function
|
|
||||||
|
|
||||||
typedef struct NodePair {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* left;
|
|
||||||
ASTNode* right;
|
|
||||||
} NodePair;
|
|
||||||
|
|
||||||
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
|
|
||||||
|
|
||||||
typedef struct NodeIndex {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* first;
|
|
||||||
ASTNode* second;
|
|
||||||
ASTNode* third;
|
|
||||||
} NodeIndex;
|
|
||||||
|
|
||||||
//variable declaration
|
|
||||||
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
|
|
||||||
|
|
||||||
typedef struct NodeVarDecl {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
Literal typeLiteral;
|
|
||||||
ASTNode* expression;
|
|
||||||
} NodeVarDecl;
|
|
||||||
|
|
||||||
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
|
|
||||||
void emitASTNodeFnCollection(ASTNode** nodeHandle);
|
|
||||||
|
|
||||||
typedef struct NodeFnCollection {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* nodes;
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
} NodeFnCollection;
|
|
||||||
|
|
||||||
//function declaration
|
|
||||||
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
|
|
||||||
|
|
||||||
typedef struct NodeFnDecl {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
ASTNode* arguments;
|
|
||||||
ASTNode* returns;
|
|
||||||
ASTNode* block;
|
|
||||||
} NodeFnDecl;
|
|
||||||
|
|
||||||
//function call
|
|
||||||
void emitASTNodeFnCall(ASTNode** nodeHandle, ASTNode* arguments);
|
|
||||||
|
|
||||||
typedef struct NodeFnCall {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* arguments;
|
|
||||||
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
|
|
||||||
} NodeFnCall;
|
|
||||||
|
|
||||||
//function return
|
|
||||||
void emitASTNodeFnReturn(ASTNode** nodeHandle, ASTNode* returns);
|
|
||||||
|
|
||||||
typedef struct NodeFnReturn {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* returns;
|
|
||||||
} NodeFnReturn;
|
|
||||||
|
|
||||||
//control flow path - if-else, while, for, break, continue, return
|
|
||||||
void emitASTNodeIf(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
|
|
||||||
void emitASTNodeWhile(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath);
|
|
||||||
void emitASTNodeFor(ASTNode** nodeHandle, ASTNode* preClause, ASTNode* condition, ASTNode* postClause, ASTNode* thenPath);
|
|
||||||
void emitASTNodeBreak(ASTNode** nodeHandle);
|
|
||||||
void emitASTNodeContinue(ASTNode** nodeHandle);
|
|
||||||
|
|
||||||
typedef struct NodeIf {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* condition;
|
|
||||||
ASTNode* thenPath;
|
|
||||||
ASTNode* elsePath;
|
|
||||||
} NodeIf;
|
|
||||||
|
|
||||||
typedef struct NodeWhile {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* condition;
|
|
||||||
ASTNode* thenPath;
|
|
||||||
} NodeWhile;
|
|
||||||
|
|
||||||
typedef struct NodeFor {
|
|
||||||
ASTNodeType type;
|
|
||||||
ASTNode* preClause;
|
|
||||||
ASTNode* condition;
|
|
||||||
ASTNode* postClause;
|
|
||||||
ASTNode* thenPath;
|
|
||||||
} NodeFor;
|
|
||||||
|
|
||||||
typedef struct NodeBreak {
|
|
||||||
ASTNodeType type;
|
|
||||||
} NodeBreak;
|
|
||||||
|
|
||||||
typedef struct NodeContinue {
|
|
||||||
ASTNodeType type;
|
|
||||||
} NodeContinue;
|
|
||||||
|
|
||||||
//pre-post increment/decrement
|
|
||||||
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier);
|
|
||||||
void emitASTNodePrefixDecrement(ASTNode** nodeHandle, Literal identifier);
|
|
||||||
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier);
|
|
||||||
void emitASTNodePostfixDecrement(ASTNode** nodeHandle, Literal identifier);
|
|
||||||
|
|
||||||
typedef struct NodePrefixIncrement {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
} NodePrefixIncrement;
|
|
||||||
|
|
||||||
typedef struct NodePrefixDecrement {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
} NodePrefixDecrement;
|
|
||||||
|
|
||||||
typedef struct NodePostfixIncrement {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
} NodePostfixIncrement;
|
|
||||||
|
|
||||||
typedef struct NodePostfixDecrement {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
} NodePostfixDecrement;
|
|
||||||
|
|
||||||
//import/export a variable
|
|
||||||
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias);
|
|
||||||
void emitASTNodeExport(ASTNode** nodeHandle, Literal identifier, Literal alias);
|
|
||||||
|
|
||||||
typedef struct NodeImport {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
Literal alias;
|
|
||||||
} NodeImport;
|
|
||||||
|
|
||||||
typedef struct NodeExport {
|
|
||||||
ASTNodeType type;
|
|
||||||
Literal identifier;
|
|
||||||
Literal alias;
|
|
||||||
} NodeExport;
|
|
||||||
|
|
||||||
union _node {
|
|
||||||
ASTNodeType type;
|
|
||||||
NodeLiteral atomic;
|
|
||||||
NodeUnary unary;
|
|
||||||
NodeBinary binary;
|
|
||||||
NodeGrouping grouping;
|
|
||||||
NodeBlock block;
|
|
||||||
NodeCompound compound;
|
|
||||||
NodePair pair;
|
|
||||||
NodeIndex index;
|
|
||||||
NodeVarDecl varDecl;
|
|
||||||
NodeFnCollection fnCollection;
|
|
||||||
NodeFnDecl fnDecl;
|
|
||||||
NodeFnCall fnCall;
|
|
||||||
NodeFnReturn returns;
|
|
||||||
NodeIf pathIf; //TODO: rename these to ifStmt?
|
|
||||||
NodeWhile pathWhile;
|
|
||||||
NodeFor pathFor;
|
|
||||||
NodeBreak pathBreak;
|
|
||||||
NodeContinue pathContinue;
|
|
||||||
NodePrefixIncrement prefixIncrement;
|
|
||||||
NodePrefixDecrement prefixDecrement;
|
|
||||||
NodePostfixIncrement postfixIncrement;
|
|
||||||
NodePostfixDecrement postfixDecrement;
|
|
||||||
NodeImport import;
|
|
||||||
NodeExport export;
|
|
||||||
};
|
|
||||||
|
|
||||||
TOY_API void freeASTNode(ASTNode* node);
|
|
||||||
-1348
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "interpreter.h"
|
|
||||||
|
|
||||||
int _index(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
int _set(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
int _get(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
int _push(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
int _pop(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
int _length(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
int _clear(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
-1200
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
#include "opcodes.h"
|
|
||||||
#include "ast_node.h"
|
|
||||||
#include "literal_array.h"
|
|
||||||
|
|
||||||
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
|
||||||
typedef struct Compiler {
|
|
||||||
LiteralArray literalCache;
|
|
||||||
unsigned char* bytecode;
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
} Compiler;
|
|
||||||
|
|
||||||
TOY_API void initCompiler(Compiler* compiler);
|
|
||||||
TOY_API void writeCompiler(Compiler* compiler, ASTNode* node);
|
|
||||||
TOY_API void freeCompiler(Compiler* compiler);
|
|
||||||
|
|
||||||
//embed the header, data section, code section, function section, etc.
|
|
||||||
TOY_API unsigned char* collateCompiler(Compiler* compiler, int* size);
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
//NOTE: you need both font AND background for these to work
|
|
||||||
|
|
||||||
//fonts color
|
|
||||||
#define FONT_BLACK "\033[30;"
|
|
||||||
#define FONT_RED "\033[31;"
|
|
||||||
#define FONT_GREEN "\033[32;"
|
|
||||||
#define FONT_YELLOW "\033[33;"
|
|
||||||
#define FONT_BLUE "\033[34;"
|
|
||||||
#define FONT_PURPLE "\033[35;"
|
|
||||||
#define FONT_DGREEN "\033[6;"
|
|
||||||
#define FONT_WHITE "\033[7;"
|
|
||||||
#define FONT_CYAN "\x1b[36m"
|
|
||||||
|
|
||||||
//background color
|
|
||||||
#define BACK_BLACK "40m"
|
|
||||||
#define BACK_RED "41m"
|
|
||||||
#define BACK_GREEN "42m"
|
|
||||||
#define BACK_YELLOW "43m"
|
|
||||||
#define BACK_BLUE "44m"
|
|
||||||
#define BACK_PURPLE "45m"
|
|
||||||
#define BACK_DGREEN "46m"
|
|
||||||
#define BACK_WHITE "47m"
|
|
||||||
|
|
||||||
//useful
|
|
||||||
#define NOTICE FONT_GREEN BACK_BLACK
|
|
||||||
#define WARN FONT_YELLOW BACK_BLACK
|
|
||||||
#define ERROR FONT_RED BACK_BLACK
|
|
||||||
#define RESET "\033[0m"
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
#include "literal.h"
|
|
||||||
#include "literal_array.h"
|
|
||||||
#include "literal_dictionary.h"
|
|
||||||
#include "scope.h"
|
|
||||||
|
|
||||||
typedef void (*PrintFn)(const char*);
|
|
||||||
|
|
||||||
//the interpreter acts depending on the bytecode instructions
|
|
||||||
typedef struct Interpreter {
|
|
||||||
//input
|
|
||||||
unsigned char* bytecode;
|
|
||||||
int length;
|
|
||||||
int count;
|
|
||||||
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
|
||||||
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
|
||||||
|
|
||||||
//operation
|
|
||||||
Scope* scope;
|
|
||||||
LiteralArray stack;
|
|
||||||
|
|
||||||
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
|
|
||||||
LiteralDictionary* exportTypes;
|
|
||||||
LiteralDictionary* hooks;
|
|
||||||
|
|
||||||
//debug outputs
|
|
||||||
PrintFn printOutput;
|
|
||||||
PrintFn assertOutput;
|
|
||||||
PrintFn errorOutput;
|
|
||||||
|
|
||||||
int depth; //don't overflow
|
|
||||||
bool panic;
|
|
||||||
} Interpreter;
|
|
||||||
|
|
||||||
//native API
|
|
||||||
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
|
|
||||||
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
|
|
||||||
|
|
||||||
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
|
|
||||||
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
|
|
||||||
|
|
||||||
TOY_API bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* arguments, LiteralArray* returns);
|
|
||||||
TOY_API bool callFn(Interpreter* interpreter, char* name, LiteralArray* arguments, LiteralArray* returns);
|
|
||||||
|
|
||||||
//utilities for the host program
|
|
||||||
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
|
|
||||||
TOY_API void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput);
|
|
||||||
TOY_API void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
|
|
||||||
TOY_API void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
|
|
||||||
|
|
||||||
//main access
|
|
||||||
TOY_API void initInterpreter(Interpreter* interpreter); //start of program
|
|
||||||
TOY_API void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
|
|
||||||
TOY_API void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
|
||||||
TOY_API void freeInterpreter(Interpreter* interpreter); //end of program
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#include "keyword_types.h"
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
KeywordType keywordTypes[] = {
|
|
||||||
//type keywords
|
|
||||||
{TOKEN_NULL, "null"},
|
|
||||||
{TOKEN_BOOLEAN, "bool"},
|
|
||||||
{TOKEN_INTEGER, "int"},
|
|
||||||
{TOKEN_FLOAT, "float"},
|
|
||||||
{TOKEN_STRING, "string"},
|
|
||||||
{TOKEN_FUNCTION, "fn"},
|
|
||||||
{TOKEN_OPAQUE, "opaque"},
|
|
||||||
{TOKEN_ANY, "any"},
|
|
||||||
|
|
||||||
//other keywords
|
|
||||||
{TOKEN_AS, "as"},
|
|
||||||
{TOKEN_ASSERT, "assert"},
|
|
||||||
{TOKEN_BREAK, "break"},
|
|
||||||
{TOKEN_CLASS, "class"},
|
|
||||||
{TOKEN_CONST, "const"},
|
|
||||||
{TOKEN_CONTINUE, "continue"},
|
|
||||||
{TOKEN_DO, "do"},
|
|
||||||
{TOKEN_ELSE, "else"},
|
|
||||||
{TOKEN_EXPORT, "export"},
|
|
||||||
{TOKEN_FOR, "for"},
|
|
||||||
{TOKEN_FOREACH, "foreach"},
|
|
||||||
{TOKEN_IF, "if"},
|
|
||||||
{TOKEN_IMPORT, "import"},
|
|
||||||
{TOKEN_IN, "in"},
|
|
||||||
{TOKEN_OF, "of"},
|
|
||||||
{TOKEN_PRINT, "print"},
|
|
||||||
{TOKEN_RETURN, "return"},
|
|
||||||
{TOKEN_TYPE, "type"},
|
|
||||||
{TOKEN_ASTYPE, "astype"},
|
|
||||||
{TOKEN_TYPEOF, "typeof"},
|
|
||||||
{TOKEN_VAR, "var"},
|
|
||||||
{TOKEN_WHILE, "while"},
|
|
||||||
|
|
||||||
//literal values
|
|
||||||
{TOKEN_LITERAL_TRUE, "true"},
|
|
||||||
{TOKEN_LITERAL_FALSE, "false"},
|
|
||||||
|
|
||||||
//meta tokens
|
|
||||||
{TOKEN_PASS, NULL},
|
|
||||||
{TOKEN_ERROR, NULL},
|
|
||||||
|
|
||||||
{TOKEN_EOF, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
char* findKeywordByType(TokenType type) {
|
|
||||||
if (type == TOKEN_EOF) {
|
|
||||||
return "EOF";
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; keywordTypes[i].keyword; i++) {
|
|
||||||
if (keywordTypes[i].type == type) {
|
|
||||||
return keywordTypes[i].keyword;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType findTypeByKeyword(const char* keyword) {
|
|
||||||
const int length = strlen(keyword);
|
|
||||||
|
|
||||||
for (int i = 0; keywordTypes[i].keyword; i++) {
|
|
||||||
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
|
|
||||||
return keywordTypes[i].type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TOKEN_EOF;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "token_types.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
TokenType type;
|
|
||||||
char* keyword;
|
|
||||||
} KeywordType;
|
|
||||||
|
|
||||||
extern KeywordType keywordTypes[];
|
|
||||||
|
|
||||||
char* findKeywordByType(TokenType type);
|
|
||||||
|
|
||||||
TokenType findTypeByKeyword(const char* keyword);
|
|
||||||
-348
@@ -1,348 +0,0 @@
|
|||||||
#include "lexer.h"
|
|
||||||
#include "console_colors.h"
|
|
||||||
#include "keyword_types.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
//static generic utility functions
|
|
||||||
static void cleanLexer(Lexer* lexer) {
|
|
||||||
lexer->source = NULL;
|
|
||||||
lexer->start = 0;
|
|
||||||
lexer->current = 0;
|
|
||||||
lexer->line = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAtEnd(Lexer* lexer) {
|
|
||||||
return lexer->source[lexer->current] == '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static char peek(Lexer* lexer) {
|
|
||||||
return lexer->source[lexer->current];
|
|
||||||
}
|
|
||||||
|
|
||||||
static char peekNext(Lexer* lexer) {
|
|
||||||
if (isAtEnd(lexer)) return '\0';
|
|
||||||
return lexer->source[lexer->current + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static char advance(Lexer* lexer) {
|
|
||||||
if (isAtEnd(lexer)) {
|
|
||||||
return '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
//new line
|
|
||||||
if (lexer->source[lexer->current] == '\n') {
|
|
||||||
lexer->line++;
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer->current++;
|
|
||||||
return lexer->source[lexer->current - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eatWhitespace(Lexer* lexer) {
|
|
||||||
const char c = peek(lexer);
|
|
||||||
|
|
||||||
switch(c) {
|
|
||||||
case ' ':
|
|
||||||
case '\r':
|
|
||||||
case '\n':
|
|
||||||
case '\t':
|
|
||||||
advance(lexer);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//comments
|
|
||||||
case '/':
|
|
||||||
//eat the line
|
|
||||||
if (peekNext(lexer) == '/') {
|
|
||||||
while (advance(lexer) != '\n' && !isAtEnd(lexer));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//eat the block
|
|
||||||
if (peekNext(lexer) == '*') {
|
|
||||||
advance(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//tail recursion
|
|
||||||
eatWhitespace(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isDigit(Lexer* lexer) {
|
|
||||||
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAlpha(Lexer* lexer) {
|
|
||||||
return
|
|
||||||
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
|
||||||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
|
||||||
peek(lexer) == '_'
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool match(Lexer* lexer, char c) {
|
|
||||||
if (peek(lexer) == c) {
|
|
||||||
advance(lexer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//token generators
|
|
||||||
static Token makeErrorToken(Lexer* lexer, char* msg) {
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
token.type = TOKEN_ERROR;
|
|
||||||
token.lexeme = msg;
|
|
||||||
token.length = strlen(msg);
|
|
||||||
token.line = lexer->line;
|
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (command.verbose) {
|
|
||||||
printf("err:");
|
|
||||||
printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token makeToken(Lexer* lexer, TokenType type) {
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
token.type = type;
|
|
||||||
token.length = lexer->current - lexer->start;
|
|
||||||
token.lexeme = &lexer->source[lexer->current - token.length];
|
|
||||||
token.line = lexer->line;
|
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
|
||||||
if (command.verbose) {
|
|
||||||
printf("tok:");
|
|
||||||
printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token makeIntegerOrFloat(Lexer* lexer) {
|
|
||||||
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
|
|
||||||
|
|
||||||
while(isDigit(lexer)) advance(lexer);
|
|
||||||
|
|
||||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
|
||||||
type = TOKEN_LITERAL_FLOAT;
|
|
||||||
advance(lexer);
|
|
||||||
while(isDigit(lexer)) advance(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
token.type = type;
|
|
||||||
token.lexeme = &lexer->source[lexer->start];
|
|
||||||
token.length = lexer->current - lexer->start;
|
|
||||||
token.line = lexer->line;
|
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (command.verbose) {
|
|
||||||
if (type == TOKEN_LITERAL_INTEGER) {
|
|
||||||
printf("int:");
|
|
||||||
} else {
|
|
||||||
printf("flt:");
|
|
||||||
}
|
|
||||||
printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token makeString(Lexer* lexer, char terminator) {
|
|
||||||
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
|
||||||
advance(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
advance(lexer); //eat terminator
|
|
||||||
|
|
||||||
if (isAtEnd(lexer)) {
|
|
||||||
return makeErrorToken(lexer, "Unterminated string");
|
|
||||||
}
|
|
||||||
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
token.type = TOKEN_LITERAL_STRING;
|
|
||||||
token.lexeme = &lexer->source[lexer->start + 1];
|
|
||||||
token.length = lexer->current - lexer->start - 2;
|
|
||||||
token.line = lexer->line;
|
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (command.verbose) {
|
|
||||||
printf("str:");
|
|
||||||
printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token makeKeywordOrIdentifier(Lexer* lexer) {
|
|
||||||
advance(lexer); //first letter can only be alpha
|
|
||||||
|
|
||||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
|
||||||
advance(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
//scan for a keyword
|
|
||||||
for (int i = 0; keywordTypes[i].keyword; i++) {
|
|
||||||
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
token.type = keywordTypes[i].type;
|
|
||||||
token.lexeme = &lexer->source[lexer->start];
|
|
||||||
token.length = lexer->current - lexer->start;
|
|
||||||
token.line = lexer->line;
|
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (command.verbose) {
|
|
||||||
printf("kwd:");
|
|
||||||
printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//return an identifier
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
token.type = TOKEN_IDENTIFIER;
|
|
||||||
token.lexeme = &lexer->source[lexer->start];
|
|
||||||
token.length = lexer->current - lexer->start;
|
|
||||||
token.line = lexer->line;
|
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (command.verbose) {
|
|
||||||
printf("idf:");
|
|
||||||
printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
void initLexer(Lexer* lexer, char* source) {
|
|
||||||
cleanLexer(lexer);
|
|
||||||
|
|
||||||
lexer->source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token scanLexer(Lexer* lexer) {
|
|
||||||
eatWhitespace(lexer);
|
|
||||||
|
|
||||||
lexer->start = lexer->current;
|
|
||||||
|
|
||||||
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
|
|
||||||
|
|
||||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
|
||||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
|
||||||
|
|
||||||
char c = advance(lexer);
|
|
||||||
|
|
||||||
switch(c) {
|
|
||||||
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
|
|
||||||
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
|
|
||||||
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
|
|
||||||
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
|
|
||||||
case '[': return makeToken(lexer, TOKEN_BRACKET_LEFT);
|
|
||||||
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
|
|
||||||
|
|
||||||
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
|
|
||||||
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
|
|
||||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
|
|
||||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
|
|
||||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
|
|
||||||
|
|
||||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
|
|
||||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
|
|
||||||
|
|
||||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
|
|
||||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
|
|
||||||
|
|
||||||
case '&': //TOKEN_AND not used
|
|
||||||
if (advance(lexer) != '&') {
|
|
||||||
return makeErrorToken(lexer, "Unexpected '&'");
|
|
||||||
} else {
|
|
||||||
return makeToken(lexer, TOKEN_AND);
|
|
||||||
}
|
|
||||||
|
|
||||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
|
|
||||||
|
|
||||||
case ':': return makeToken(lexer, TOKEN_COLON);
|
|
||||||
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
|
||||||
case ',': return makeToken(lexer, TOKEN_COMMA);
|
|
||||||
case '.':
|
|
||||||
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
|
||||||
advance(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
return makeToken(lexer, TOKEN_REST);
|
|
||||||
}
|
|
||||||
return makeToken(lexer, TOKEN_DOT);
|
|
||||||
|
|
||||||
case '"':
|
|
||||||
return makeString(lexer, c);
|
|
||||||
//TODO: possibly support interpolated strings
|
|
||||||
|
|
||||||
default: {
|
|
||||||
char buffer[128];
|
|
||||||
snprintf(buffer, 128, "Unexpected token: %c", c);
|
|
||||||
return makeErrorToken(lexer, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void trim(char** s, int* l) { //all this to remove a newline?
|
|
||||||
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
|
||||||
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
|
||||||
}
|
|
||||||
|
|
||||||
//for debugging
|
|
||||||
void printToken(Token* token) {
|
|
||||||
if (token->type == TOKEN_ERROR) {
|
|
||||||
printf(ERROR "Error\t%d\t%.*s\n" RESET, token->line, token->length, token->lexeme);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\t%d\t%d\t", token->type, token->line);
|
|
||||||
|
|
||||||
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
|
|
||||||
printf("%.*s\t", token->length, token->lexeme);
|
|
||||||
} else {
|
|
||||||
char* keyword = findKeywordByType(token->type);
|
|
||||||
|
|
||||||
if (keyword != NULL) {
|
|
||||||
printf("%s", keyword);
|
|
||||||
} else {
|
|
||||||
char* str = token->lexeme;
|
|
||||||
int length = token->length;
|
|
||||||
trim(&str, &length);
|
|
||||||
printf("%.*s", length, str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
#include "token_types.h"
|
|
||||||
|
|
||||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
|
||||||
typedef struct {
|
|
||||||
char* source;
|
|
||||||
int start; //start of the token
|
|
||||||
int current; //current position of the lexer
|
|
||||||
int line; //track this for error handling
|
|
||||||
} Lexer;
|
|
||||||
|
|
||||||
//tokens are intermediaries between lexers and parsers
|
|
||||||
typedef struct {
|
|
||||||
TokenType type;
|
|
||||||
char* lexeme;
|
|
||||||
int length;
|
|
||||||
int line;
|
|
||||||
} Token;
|
|
||||||
|
|
||||||
TOY_API void initLexer(Lexer* lexer, char* source);
|
|
||||||
Token scanLexer(Lexer* lexer);
|
|
||||||
|
|
||||||
//for debugging
|
|
||||||
void printToken(Token* token);
|
|
||||||
@@ -1,695 +0,0 @@
|
|||||||
#include "literal.h"
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include "literal_array.h"
|
|
||||||
#include "literal_dictionary.h"
|
|
||||||
#include "scope.h"
|
|
||||||
|
|
||||||
#include "console_colors.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
//hash util functions
|
|
||||||
static unsigned int hashString(const char* string, int length) {
|
|
||||||
unsigned int hash = 2166136261u;
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
hash *= string[i];
|
|
||||||
hash ^= 16777619;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int hashUInt(unsigned int x) {
|
|
||||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
|
||||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
|
||||||
x = (x >> 16) ^ x;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
void freeLiteral(Literal literal) {
|
|
||||||
//refstrings
|
|
||||||
if (IS_STRING(literal)) {
|
|
||||||
deleteRefString(AS_STRING(literal));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_IDENTIFIER(literal)) {
|
|
||||||
deleteRefString(AS_IDENTIFIER(literal));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//compounds
|
|
||||||
if (IS_ARRAY(literal) || literal.type == LITERAL_DICTIONARY_INTERMEDIATE || literal.type == LITERAL_TYPE_INTERMEDIATE) {
|
|
||||||
freeLiteralArray(AS_ARRAY(literal));
|
|
||||||
FREE(LiteralArray, AS_ARRAY(literal));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_DICTIONARY(literal)) {
|
|
||||||
freeLiteralDictionary(AS_DICTIONARY(literal));
|
|
||||||
FREE(LiteralDictionary, AS_DICTIONARY(literal));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//complex literals
|
|
||||||
if (IS_FUNCTION(literal)) {
|
|
||||||
popScope(AS_FUNCTION(literal).scope);
|
|
||||||
AS_FUNCTION(literal).scope = NULL;
|
|
||||||
FREE_ARRAY(unsigned char, AS_FUNCTION(literal).bytecode, AS_FUNCTION(literal).length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_TYPE(literal)) {
|
|
||||||
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
|
||||||
freeLiteral(((Literal*)(AS_TYPE(literal).subtypes))[i]);
|
|
||||||
}
|
|
||||||
FREE_ARRAY(Literal, AS_TYPE(literal).subtypes, AS_TYPE(literal).capacity);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isTruthy(Literal x) {
|
|
||||||
if (IS_NULL(x)) {
|
|
||||||
fprintf(stderr, ERROR "ERROR: Null is neither true nor false\n" RESET);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_BOOLEAN(x)) {
|
|
||||||
return AS_BOOLEAN(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal _toStringLiteral(RefString* ptr) {
|
|
||||||
return ((Literal){LITERAL_STRING, { .string.ptr = ptr }});
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal _toIdentifierLiteral(RefString* ptr) {
|
|
||||||
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = ptr, .identifier.hash = hashString(toCString(ptr), lengthRefString(ptr)) }});
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal* _typePushSubtype(Literal* lit, Literal subtype) {
|
|
||||||
//grow the subtype array
|
|
||||||
if (AS_TYPE(*lit).count + 1 > AS_TYPE(*lit).capacity) {
|
|
||||||
int oldCapacity = AS_TYPE(*lit).capacity;
|
|
||||||
|
|
||||||
AS_TYPE(*lit).capacity = GROW_CAPACITY(oldCapacity);
|
|
||||||
AS_TYPE(*lit).subtypes = GROW_ARRAY(Literal, AS_TYPE(*lit).subtypes, oldCapacity, AS_TYPE(*lit).capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
//actually push
|
|
||||||
((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count++ ] = subtype;
|
|
||||||
return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal copyLiteral(Literal original) {
|
|
||||||
switch(original.type) {
|
|
||||||
case LITERAL_NULL:
|
|
||||||
case LITERAL_BOOLEAN:
|
|
||||||
case LITERAL_INTEGER:
|
|
||||||
case LITERAL_FLOAT:
|
|
||||||
//no copying needed
|
|
||||||
return original;
|
|
||||||
|
|
||||||
case LITERAL_STRING: {
|
|
||||||
return TO_STRING_LITERAL(copyRefString(AS_STRING(original)));
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_ARRAY: {
|
|
||||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
|
||||||
initLiteralArray(array);
|
|
||||||
|
|
||||||
//copy each element
|
|
||||||
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
|
||||||
pushLiteralArray(array, AS_ARRAY(original)->literals[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TO_ARRAY_LITERAL(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_DICTIONARY: {
|
|
||||||
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
|
||||||
initLiteralDictionary(dictionary);
|
|
||||||
|
|
||||||
//copy each entry
|
|
||||||
for (int i = 0; i < AS_DICTIONARY(original)->capacity; i++) {
|
|
||||||
if ( !IS_NULL(AS_DICTIONARY(original)->entries[i].key) ) {
|
|
||||||
setLiteralDictionary(dictionary, AS_DICTIONARY(original)->entries[i].key, AS_DICTIONARY(original)->entries[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TO_DICTIONARY_LITERAL(dictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION: {
|
|
||||||
unsigned char* buffer = ALLOCATE(unsigned char, AS_FUNCTION(original).length);
|
|
||||||
memcpy(buffer, AS_FUNCTION(original).bytecode, AS_FUNCTION(original).length);
|
|
||||||
|
|
||||||
Literal literal = TO_FUNCTION_LITERAL(buffer, AS_FUNCTION(original).length);
|
|
||||||
AS_FUNCTION(literal).scope = copyScope(AS_FUNCTION(original).scope);
|
|
||||||
|
|
||||||
return literal;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_IDENTIFIER: {
|
|
||||||
return TO_IDENTIFIER_LITERAL(copyRefString(AS_IDENTIFIER(original)));
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_TYPE: {
|
|
||||||
Literal lit = TO_TYPE_LITERAL(AS_TYPE(original).typeOf, AS_TYPE(original).constant);
|
|
||||||
|
|
||||||
for (int i = 0; i < AS_TYPE(original).count; i++) {
|
|
||||||
TYPE_PUSH_SUBTYPE(&lit, copyLiteral( ((Literal*)(AS_TYPE(original).subtypes))[i] ));
|
|
||||||
}
|
|
||||||
|
|
||||||
return lit;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_OPAQUE: {
|
|
||||||
return original; //literally a shallow copy
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_DICTIONARY_INTERMEDIATE: {
|
|
||||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
|
||||||
initLiteralArray(array);
|
|
||||||
|
|
||||||
//copy each element
|
|
||||||
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
|
||||||
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
|
|
||||||
pushLiteralArray(array, literal);
|
|
||||||
freeLiteral(literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal ret = TO_ARRAY_LITERAL(array);
|
|
||||||
ret.type = LITERAL_DICTIONARY_INTERMEDIATE;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_TYPE_INTERMEDIATE: {
|
|
||||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
|
||||||
initLiteralArray(array);
|
|
||||||
|
|
||||||
//copy each element
|
|
||||||
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
|
||||||
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
|
|
||||||
pushLiteralArray(array, literal);
|
|
||||||
freeLiteral(literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal ret = TO_ARRAY_LITERAL(array);
|
|
||||||
ret.type = LITERAL_TYPE_INTERMEDIATE;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
|
||||||
case LITERAL_FUNCTION_NATIVE:
|
|
||||||
//no copying possible
|
|
||||||
return original;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf(stderr, ERROR "ERROR: Can't copy that literal type: %d\n" RESET, original.type);
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool literalsAreEqual(Literal lhs, Literal rhs) {
|
|
||||||
//utility for other things
|
|
||||||
if (lhs.type != rhs.type) {
|
|
||||||
// ints and floats are compatible
|
|
||||||
if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) {
|
|
||||||
if (IS_INTEGER(lhs)) {
|
|
||||||
return AS_INTEGER(lhs) + AS_FLOAT(rhs);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return AS_FLOAT(lhs) + AS_INTEGER(rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(lhs.type) {
|
|
||||||
case LITERAL_NULL:
|
|
||||||
return true; //can only be true because of the check above
|
|
||||||
|
|
||||||
case LITERAL_BOOLEAN:
|
|
||||||
return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs);
|
|
||||||
|
|
||||||
case LITERAL_INTEGER:
|
|
||||||
return AS_INTEGER(lhs) == AS_INTEGER(rhs);
|
|
||||||
|
|
||||||
case LITERAL_FLOAT:
|
|
||||||
return AS_FLOAT(lhs) == AS_FLOAT(rhs);
|
|
||||||
|
|
||||||
case LITERAL_STRING:
|
|
||||||
return equalsRefString(AS_STRING(lhs), AS_STRING(rhs));
|
|
||||||
|
|
||||||
case LITERAL_ARRAY:
|
|
||||||
case LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
|
||||||
case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
|
||||||
//mismatched sizes
|
|
||||||
if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//mismatched elements (in order)
|
|
||||||
for (int i = 0; i < AS_ARRAY(lhs)->count; i++) {
|
|
||||||
if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case LITERAL_DICTIONARY:
|
|
||||||
//relatively slow, especially when nested
|
|
||||||
for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) {
|
|
||||||
if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
|
||||||
//check it exists in rhs
|
|
||||||
if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//compare the values
|
|
||||||
Literal val = getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
|
|
||||||
if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, val)) {
|
|
||||||
freeLiteral(val);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
freeLiteral(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION:
|
|
||||||
case LITERAL_FUNCTION_NATIVE:
|
|
||||||
return false; //functions are never equal
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_IDENTIFIER:
|
|
||||||
//check shortcuts
|
|
||||||
if (HASH_I(lhs) != HASH_I(rhs)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return equalsRefString(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs));
|
|
||||||
|
|
||||||
case LITERAL_TYPE:
|
|
||||||
//check types
|
|
||||||
if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//const don't match
|
|
||||||
if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check subtypes
|
|
||||||
if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check array|dictionary signatures are the same (in order)
|
|
||||||
if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) {
|
|
||||||
for (int i = 0; i < AS_TYPE(lhs).count; i++) {
|
|
||||||
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case LITERAL_OPAQUE:
|
|
||||||
return false; //IDK what this is!
|
|
||||||
|
|
||||||
case LITERAL_ANY:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION_INTERMEDIATE:
|
|
||||||
fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET);
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//should never be seen
|
|
||||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashLiteral(Literal lit) {
|
|
||||||
switch(lit.type) {
|
|
||||||
case LITERAL_NULL:
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case LITERAL_BOOLEAN:
|
|
||||||
return AS_BOOLEAN(lit) ? 1 : 0;
|
|
||||||
|
|
||||||
case LITERAL_INTEGER:
|
|
||||||
return hashUInt((unsigned int)AS_INTEGER(lit));
|
|
||||||
|
|
||||||
case LITERAL_FLOAT:
|
|
||||||
return hashUInt(*(unsigned int*)(&AS_FLOAT(lit)));
|
|
||||||
|
|
||||||
case LITERAL_STRING:
|
|
||||||
return hashString(toCString(AS_STRING(lit)), lengthRefString(AS_STRING(lit)));
|
|
||||||
|
|
||||||
case LITERAL_ARRAY: {
|
|
||||||
unsigned int res = 0;
|
|
||||||
for (int i = 0; i < AS_ARRAY(lit)->count; i++) {
|
|
||||||
res += hashLiteral(AS_ARRAY(lit)->literals[i]);
|
|
||||||
}
|
|
||||||
return hashUInt(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_DICTIONARY: {
|
|
||||||
unsigned int res = 0;
|
|
||||||
for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) {
|
|
||||||
if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
|
||||||
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key);
|
|
||||||
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hashUInt(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION:
|
|
||||||
case LITERAL_FUNCTION_NATIVE:
|
|
||||||
return 0; //TODO: find a way to hash these properly
|
|
||||||
|
|
||||||
case LITERAL_IDENTIFIER:
|
|
||||||
return HASH_I(lit); //pre-computed
|
|
||||||
|
|
||||||
case LITERAL_TYPE:
|
|
||||||
return AS_TYPE(lit).typeOf; //nothing else I can do
|
|
||||||
|
|
||||||
case LITERAL_OPAQUE:
|
|
||||||
case LITERAL_ANY:
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//should never bee seen
|
|
||||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//utils
|
|
||||||
static void stdoutWrapper(const char* output) {
|
|
||||||
printf("%s", output);
|
|
||||||
}
|
|
||||||
|
|
||||||
//buffer the prints
|
|
||||||
static char* globalPrintBuffer = NULL;
|
|
||||||
static size_t globalPrintCapacity = 0;
|
|
||||||
static size_t globalPrintCount = 0;
|
|
||||||
|
|
||||||
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
|
||||||
static char quotes = 0; //set to 0 to not show string quotes
|
|
||||||
|
|
||||||
static void printToBuffer(const char* str) {
|
|
||||||
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
|
|
||||||
int oldCapacity = globalPrintCapacity;
|
|
||||||
|
|
||||||
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
|
|
||||||
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
|
|
||||||
globalPrintCount += strlen(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
void printLiteral(Literal literal) {
|
|
||||||
printLiteralCustom(literal, stdoutWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
|
||||||
switch(literal.type) {
|
|
||||||
case LITERAL_NULL:
|
|
||||||
printFn("null");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_BOOLEAN:
|
|
||||||
printFn(AS_BOOLEAN(literal) ? "true" : "false");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_INTEGER: {
|
|
||||||
char buffer[256];
|
|
||||||
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
|
|
||||||
printFn(buffer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_FLOAT: {
|
|
||||||
char buffer[256];
|
|
||||||
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
|
|
||||||
printFn(buffer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_STRING: {
|
|
||||||
char buffer[MAX_STRING_LENGTH];
|
|
||||||
if (!quotes) {
|
|
||||||
snprintf(buffer, MAX_STRING_LENGTH, "%.*s", lengthRefString(AS_STRING(literal)), toCString(AS_STRING(literal)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, lengthRefString(AS_STRING(literal)), toCString(AS_STRING(literal)), quotes);
|
|
||||||
}
|
|
||||||
printFn(buffer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_ARRAY: {
|
|
||||||
LiteralArray* ptr = AS_ARRAY(literal);
|
|
||||||
|
|
||||||
//hold potential parent-call buffers on the C stack
|
|
||||||
char* cacheBuffer = globalPrintBuffer;
|
|
||||||
globalPrintBuffer = NULL;
|
|
||||||
int cacheCapacity = globalPrintCapacity;
|
|
||||||
globalPrintCapacity = 0;
|
|
||||||
int cacheCount = globalPrintCount;
|
|
||||||
globalPrintCount = 0;
|
|
||||||
|
|
||||||
//print the contents to the global buffer
|
|
||||||
printToBuffer("[");
|
|
||||||
for (int i = 0; i < ptr->count; i++) {
|
|
||||||
quotes = '"';
|
|
||||||
printLiteralCustom(ptr->literals[i], printToBuffer);
|
|
||||||
|
|
||||||
if (i + 1 < ptr->count) {
|
|
||||||
printToBuffer(",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printToBuffer("]");
|
|
||||||
|
|
||||||
//swap the parent-call buffer back into place
|
|
||||||
char* printBuffer = globalPrintBuffer;
|
|
||||||
int printCapacity = globalPrintCapacity;
|
|
||||||
int printCount = globalPrintCount;
|
|
||||||
|
|
||||||
globalPrintBuffer = cacheBuffer;
|
|
||||||
globalPrintCapacity = cacheCapacity;
|
|
||||||
globalPrintCount = cacheCount;
|
|
||||||
|
|
||||||
//finally, output and cleanup
|
|
||||||
printFn(printBuffer);
|
|
||||||
FREE_ARRAY(char, printBuffer, printCapacity);
|
|
||||||
quotes = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_DICTIONARY: {
|
|
||||||
LiteralDictionary* ptr = AS_DICTIONARY(literal);
|
|
||||||
|
|
||||||
//hold potential parent-call buffers on the C stack
|
|
||||||
char* cacheBuffer = globalPrintBuffer;
|
|
||||||
globalPrintBuffer = NULL;
|
|
||||||
int cacheCapacity = globalPrintCapacity;
|
|
||||||
globalPrintCapacity = 0;
|
|
||||||
int cacheCount = globalPrintCount;
|
|
||||||
globalPrintCount = 0;
|
|
||||||
|
|
||||||
//print the contents to the global buffer
|
|
||||||
int delimCount = 0;
|
|
||||||
printToBuffer("[");
|
|
||||||
for (int i = 0; i < ptr->capacity; i++) {
|
|
||||||
if (IS_NULL(ptr->entries[i].key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delimCount++ > 0) {
|
|
||||||
printToBuffer(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
quotes = '"';
|
|
||||||
printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
|
||||||
printToBuffer(":");
|
|
||||||
quotes = '"';
|
|
||||||
printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
//empty dicts MUST have a ":" printed
|
|
||||||
if (ptr->count == 0) {
|
|
||||||
printToBuffer(":");
|
|
||||||
}
|
|
||||||
|
|
||||||
printToBuffer("]");
|
|
||||||
|
|
||||||
//swap the parent-call buffer back into place
|
|
||||||
char* printBuffer = globalPrintBuffer;
|
|
||||||
int printCapacity = globalPrintCapacity;
|
|
||||||
int printCount = globalPrintCount;
|
|
||||||
|
|
||||||
globalPrintBuffer = cacheBuffer;
|
|
||||||
globalPrintCapacity = cacheCapacity;
|
|
||||||
globalPrintCount = cacheCount;
|
|
||||||
|
|
||||||
//finally, output and cleanup
|
|
||||||
printFn(printBuffer);
|
|
||||||
FREE_ARRAY(char, printBuffer, printCapacity);
|
|
||||||
quotes = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
//TODO: functions
|
|
||||||
case LITERAL_FUNCTION:
|
|
||||||
case LITERAL_FUNCTION_NATIVE:
|
|
||||||
printFn("(function)");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_IDENTIFIER: {
|
|
||||||
char buffer[256];
|
|
||||||
snprintf(buffer, 256, "%.*s", lengthRefString(AS_IDENTIFIER(literal)), toCString(AS_IDENTIFIER(literal)));
|
|
||||||
printFn(buffer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_TYPE: {
|
|
||||||
//hold potential parent-call buffers on the C stack
|
|
||||||
char* cacheBuffer = globalPrintBuffer;
|
|
||||||
globalPrintBuffer = NULL;
|
|
||||||
int cacheCapacity = globalPrintCapacity;
|
|
||||||
globalPrintCapacity = 0;
|
|
||||||
int cacheCount = globalPrintCount;
|
|
||||||
globalPrintCount = 0;
|
|
||||||
|
|
||||||
//print the type correctly
|
|
||||||
printToBuffer("<");
|
|
||||||
|
|
||||||
switch(AS_TYPE(literal).typeOf) {
|
|
||||||
case LITERAL_NULL:
|
|
||||||
printToBuffer("null");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_BOOLEAN:
|
|
||||||
printToBuffer("bool");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_INTEGER:
|
|
||||||
printToBuffer("int");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_FLOAT:
|
|
||||||
printToBuffer("float");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_STRING:
|
|
||||||
printToBuffer("string");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_ARRAY:
|
|
||||||
//print all in the array
|
|
||||||
printToBuffer("[");
|
|
||||||
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
|
||||||
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
|
|
||||||
}
|
|
||||||
printToBuffer("]");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_DICTIONARY:
|
|
||||||
printToBuffer("[");
|
|
||||||
|
|
||||||
for (int i = 0; i < AS_TYPE(literal).count; i += 2) {
|
|
||||||
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
|
|
||||||
printToBuffer(":");
|
|
||||||
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
|
|
||||||
}
|
|
||||||
printToBuffer("]");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION:
|
|
||||||
printToBuffer("function");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_FUNCTION_NATIVE:
|
|
||||||
printToBuffer("native");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_IDENTIFIER:
|
|
||||||
printToBuffer("identifier");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_TYPE:
|
|
||||||
printToBuffer("type");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_OPAQUE:
|
|
||||||
printToBuffer("opaque");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_ANY:
|
|
||||||
printToBuffer("any");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//should never be seen
|
|
||||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
//const (printed last)
|
|
||||||
if (AS_TYPE(literal).constant) {
|
|
||||||
printToBuffer(" const");
|
|
||||||
}
|
|
||||||
|
|
||||||
printToBuffer(">");
|
|
||||||
|
|
||||||
//swap the parent-call buffer back into place
|
|
||||||
char* printBuffer = globalPrintBuffer;
|
|
||||||
int printCapacity = globalPrintCapacity;
|
|
||||||
int printCount = globalPrintCount;
|
|
||||||
|
|
||||||
globalPrintBuffer = cacheBuffer;
|
|
||||||
globalPrintCapacity = cacheCapacity;
|
|
||||||
globalPrintCount = cacheCount;
|
|
||||||
|
|
||||||
//finally, output and cleanup
|
|
||||||
printFn(printBuffer);
|
|
||||||
FREE_ARRAY(char, printBuffer, printCapacity);
|
|
||||||
quotes = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_TYPE_INTERMEDIATE:
|
|
||||||
case LITERAL_FUNCTION_INTERMEDIATE:
|
|
||||||
printFn("Unprintable literal found");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_OPAQUE:
|
|
||||||
printFn("(opaque)");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LITERAL_ANY:
|
|
||||||
printFn("(any)");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//should never be seen
|
|
||||||
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
|
|
||||||
#include "refstring.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
LITERAL_NULL,
|
|
||||||
LITERAL_BOOLEAN,
|
|
||||||
LITERAL_INTEGER,
|
|
||||||
LITERAL_FLOAT,
|
|
||||||
LITERAL_STRING,
|
|
||||||
LITERAL_ARRAY,
|
|
||||||
LITERAL_DICTIONARY,
|
|
||||||
LITERAL_FUNCTION,
|
|
||||||
LITERAL_IDENTIFIER,
|
|
||||||
LITERAL_TYPE,
|
|
||||||
LITERAL_OPAQUE,
|
|
||||||
LITERAL_ANY,
|
|
||||||
|
|
||||||
//these are meta-level types - not for general use
|
|
||||||
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
|
||||||
LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
|
||||||
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
|
||||||
LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
|
||||||
LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
|
||||||
} LiteralType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
LiteralType type;
|
|
||||||
union {
|
|
||||||
bool boolean;
|
|
||||||
int integer;
|
|
||||||
float number;
|
|
||||||
struct {
|
|
||||||
RefString* ptr;
|
|
||||||
//string hash?
|
|
||||||
} string;
|
|
||||||
|
|
||||||
void* array;
|
|
||||||
void* dictionary;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
void* bytecode;
|
|
||||||
void* scope;
|
|
||||||
int length;
|
|
||||||
} function;
|
|
||||||
|
|
||||||
struct { //for variable names
|
|
||||||
RefString* ptr;
|
|
||||||
int hash;
|
|
||||||
} identifier;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
LiteralType typeOf; //no longer a mask
|
|
||||||
bool constant;
|
|
||||||
void* subtypes; //for nested types caused by compounds
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
} type;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
void* ptr;
|
|
||||||
int tag; //TODO: remove tags?
|
|
||||||
} opaque;
|
|
||||||
} as;
|
|
||||||
} Literal;
|
|
||||||
|
|
||||||
#define IS_NULL(value) ((value).type == LITERAL_NULL)
|
|
||||||
#define IS_BOOLEAN(value) ((value).type == LITERAL_BOOLEAN)
|
|
||||||
#define IS_INTEGER(value) ((value).type == LITERAL_INTEGER)
|
|
||||||
#define IS_FLOAT(value) ((value).type == LITERAL_FLOAT)
|
|
||||||
#define IS_STRING(value) ((value).type == LITERAL_STRING)
|
|
||||||
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
|
|
||||||
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
|
|
||||||
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
|
|
||||||
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
|
|
||||||
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
|
|
||||||
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
|
|
||||||
#define IS_OPAQUE(value) ((value).type == LITERAL_OPAQUE)
|
|
||||||
|
|
||||||
#define AS_BOOLEAN(value) ((value).as.boolean)
|
|
||||||
#define AS_INTEGER(value) ((value).as.integer)
|
|
||||||
#define AS_FLOAT(value) ((value).as.number)
|
|
||||||
#define AS_STRING(value) ((value).as.string.ptr)
|
|
||||||
#define AS_ARRAY(value) ((LiteralArray*)((value).as.array))
|
|
||||||
#define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary))
|
|
||||||
#define AS_FUNCTION(value) ((value).as.function)
|
|
||||||
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
|
||||||
#define AS_TYPE(value) ((value).as.type)
|
|
||||||
#define AS_OPAQUE(value) ((value).as.opaque.ptr)
|
|
||||||
|
|
||||||
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
|
|
||||||
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
|
|
||||||
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
|
|
||||||
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
|
|
||||||
#define TO_STRING_LITERAL(value) _toStringLiteral(value)
|
|
||||||
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
|
|
||||||
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
|
|
||||||
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
|
||||||
#define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value)
|
|
||||||
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
|
||||||
#define TO_OPAQUE_LITERAL(value, t) ((Literal){ LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
|
|
||||||
|
|
||||||
TOY_API void freeLiteral(Literal literal);
|
|
||||||
|
|
||||||
#define IS_TRUTHY(x) _isTruthy(x)
|
|
||||||
|
|
||||||
#define MAX_STRING_LENGTH 4096
|
|
||||||
#define HASH_I(lit) ((lit).as.identifier.hash)
|
|
||||||
#define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype)
|
|
||||||
#define OPAQUE_TAG(o) o.as.opaque.tag
|
|
||||||
|
|
||||||
//BUGFIX: macros are not functions
|
|
||||||
TOY_API bool _isTruthy(Literal x);
|
|
||||||
TOY_API Literal _toStringLiteral(RefString* ptr);
|
|
||||||
TOY_API Literal _toIdentifierLiteral(RefString* ptr);
|
|
||||||
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
|
|
||||||
|
|
||||||
//utils
|
|
||||||
TOY_API Literal copyLiteral(Literal original);
|
|
||||||
TOY_API bool literalsAreEqual(Literal lhs, Literal rhs);
|
|
||||||
TOY_API int hashLiteral(Literal lit);
|
|
||||||
|
|
||||||
TOY_API void printLiteral(Literal literal);
|
|
||||||
TOY_API void printLiteralCustom(Literal literal, void (printFn)(const char*));
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#include "literal_array.h"
|
|
||||||
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
void initLiteralArray(LiteralArray* array) {
|
|
||||||
array->capacity = 0;
|
|
||||||
array->count = 0;
|
|
||||||
array->literals = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeLiteralArray(LiteralArray* array) {
|
|
||||||
//clean up memory
|
|
||||||
for(int i = 0; i < array->count; i++) {
|
|
||||||
freeLiteral(array->literals[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
FREE_ARRAY(Literal, array->literals, array->capacity);
|
|
||||||
initLiteralArray(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pushLiteralArray(LiteralArray* array, Literal literal) {
|
|
||||||
if (array->capacity < array->count + 1) {
|
|
||||||
int oldCapacity = array->capacity;
|
|
||||||
|
|
||||||
array->capacity = GROW_CAPACITY(oldCapacity);
|
|
||||||
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
array->literals[array->count] = copyLiteral(literal);
|
|
||||||
return array->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal popLiteralArray(LiteralArray* array) {
|
|
||||||
if (array->count <= 0) {
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the return
|
|
||||||
Literal ret = array->literals[array->count-1];
|
|
||||||
|
|
||||||
//null the existing data
|
|
||||||
array->literals[array->count-1] = TO_NULL_LITERAL;
|
|
||||||
|
|
||||||
array->count--;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//find a literal in the array that matches the "literal" argument
|
|
||||||
int findLiteralIndex(LiteralArray* array, Literal literal) {
|
|
||||||
for (int i = 0; i < array->count; i++) {
|
|
||||||
//not the same type
|
|
||||||
if (array->literals[i].type != literal.type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//types match?
|
|
||||||
if (literalsAreEqual(array->literals[i], literal)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
|
|
||||||
if (!IS_INTEGER(index)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int idx = AS_INTEGER(index);
|
|
||||||
|
|
||||||
if (idx < 0 || idx >= array->count) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: implicit push when referencing one-past-the-end?
|
|
||||||
|
|
||||||
freeLiteral(array->literals[idx]);
|
|
||||||
array->literals[idx] = copyLiteral(value);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal getLiteralArray(LiteralArray* array, Literal index) {
|
|
||||||
if (!IS_INTEGER(index)) {
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int idx = AS_INTEGER(index);
|
|
||||||
|
|
||||||
if (idx < 0 || idx >= array->count) {
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return copyLiteral(array->literals[idx]);
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
|
|
||||||
#include "literal.h"
|
|
||||||
|
|
||||||
typedef struct LiteralArray {
|
|
||||||
Literal* literals;
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
} LiteralArray;
|
|
||||||
|
|
||||||
TOY_API void initLiteralArray(LiteralArray* array);
|
|
||||||
TOY_API void freeLiteralArray(LiteralArray* array);
|
|
||||||
TOY_API int pushLiteralArray(LiteralArray* array, Literal literal);
|
|
||||||
TOY_API Literal popLiteralArray(LiteralArray* array);
|
|
||||||
TOY_API bool setLiteralArray(LiteralArray* array, Literal index, Literal value);
|
|
||||||
TOY_API Literal getLiteralArray(LiteralArray* array, Literal index);
|
|
||||||
|
|
||||||
int findLiteralIndex(LiteralArray* array, Literal literal);
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
#include "literal_dictionary.h"
|
|
||||||
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include "console_colors.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
//util functions
|
|
||||||
static void setEntryValues(_entry* entry, Literal key, Literal value) {
|
|
||||||
//much simpler now
|
|
||||||
freeLiteral(entry->key);
|
|
||||||
entry->key = copyLiteral(key);
|
|
||||||
|
|
||||||
freeLiteral(entry->value);
|
|
||||||
entry->value = copyLiteral(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _entry* getEntryArray(_entry* array, int capacity, Literal key, unsigned int hash, bool mustExist) {
|
|
||||||
//find "key", starting at index
|
|
||||||
unsigned int index = hash % capacity;
|
|
||||||
unsigned int start = index;
|
|
||||||
|
|
||||||
//increment once, so it can't equal start
|
|
||||||
index = (index + 1) % capacity;
|
|
||||||
|
|
||||||
//literal probing and collision checking
|
|
||||||
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
|
||||||
_entry* entry = &array[index];
|
|
||||||
|
|
||||||
if (IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
|
||||||
if (IS_NULL(entry->value) && !mustExist) {
|
|
||||||
//found a truly empty bucket
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
//else it's a tombstone - ignore
|
|
||||||
} else {
|
|
||||||
if (literalsAreEqual(key, entry->key)) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
index = (index + 1) % capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
|
||||||
//new entry space
|
|
||||||
_entry* newEntries = ALLOCATE(_entry, capacity);
|
|
||||||
|
|
||||||
for (int i = 0; i < capacity; i++) {
|
|
||||||
newEntries[i].key = TO_NULL_LITERAL;
|
|
||||||
newEntries[i].value = TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//move the old array into the new one
|
|
||||||
for (int i = 0; i < oldCapacity; i++) {
|
|
||||||
if (IS_NULL((*dictionaryHandle)[i].key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//place the key and value in the new array (reusing string memory)
|
|
||||||
_entry* entry = getEntryArray(newEntries, capacity, TO_NULL_LITERAL, hashLiteral((*dictionaryHandle)[i].key), false);
|
|
||||||
|
|
||||||
entry->key = (*dictionaryHandle)[i].key;
|
|
||||||
entry->value = (*dictionaryHandle)[i].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//clear the old array
|
|
||||||
FREE_ARRAY(_entry, *dictionaryHandle, oldCapacity);
|
|
||||||
|
|
||||||
*dictionaryHandle = newEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
|
|
||||||
//expand array if needed
|
|
||||||
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
|
|
||||||
int oldCapacity = *capacityPtr;
|
|
||||||
*capacityPtr = GROW_CAPACITY(*capacityPtr);
|
|
||||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
|
||||||
}
|
|
||||||
|
|
||||||
_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
|
||||||
|
|
||||||
//true = contains increase
|
|
||||||
if (IS_NULL(entry->key)) {
|
|
||||||
setEntryValues(entry, key, value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setEntryValues(entry, key, value);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void freeEntry(_entry* entry) {
|
|
||||||
freeLiteral(entry->key);
|
|
||||||
freeLiteral(entry->value);
|
|
||||||
entry->key = TO_NULL_LITERAL;
|
|
||||||
entry->value = TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void freeEntryArray(_entry* array, int capacity) {
|
|
||||||
if (array == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < capacity; i++) {
|
|
||||||
if (!IS_NULL(array[i].key)) {
|
|
||||||
freeEntry(&array[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FREE_ARRAY(_entry, array, capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
void initLiteralDictionary(LiteralDictionary* dictionary) {
|
|
||||||
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
|
||||||
dictionary->entries = NULL;
|
|
||||||
dictionary->capacity = GROW_CAPACITY(0);
|
|
||||||
dictionary->contains = 0;
|
|
||||||
dictionary->count = 0;
|
|
||||||
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeLiteralDictionary(LiteralDictionary* dictionary) {
|
|
||||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
|
||||||
dictionary->capacity = 0;
|
|
||||||
dictionary->contains = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
|
|
||||||
if (IS_NULL(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
|
||||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_OPAQUE(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
|
|
||||||
|
|
||||||
if (increment) {
|
|
||||||
dictionary->contains++;
|
|
||||||
dictionary->count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
|
||||||
if (IS_NULL(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
|
||||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_OPAQUE(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
|
||||||
|
|
||||||
if (entry != NULL) {
|
|
||||||
return copyLiteral(entry->value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
|
||||||
if (IS_NULL(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (remove)\n" RESET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
|
||||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (remove)\n" RESET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_OPAQUE(key)) {
|
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
|
||||||
|
|
||||||
if (entry != NULL) {
|
|
||||||
freeEntry(entry);
|
|
||||||
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
|
|
||||||
dictionary->count--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
|
||||||
//null & not tombstoned
|
|
||||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
|
|
||||||
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
|
|
||||||
#include "literal.h"
|
|
||||||
|
|
||||||
//TODO: benchmark this
|
|
||||||
#define DICTIONARY_MAX_LOAD 0.75
|
|
||||||
|
|
||||||
typedef struct _entry {
|
|
||||||
Literal key;
|
|
||||||
Literal value;
|
|
||||||
} _entry;
|
|
||||||
|
|
||||||
typedef struct LiteralDictionary {
|
|
||||||
_entry* entries;
|
|
||||||
int capacity;
|
|
||||||
int count;
|
|
||||||
int contains; //count + tombstones, for internal use
|
|
||||||
} LiteralDictionary;
|
|
||||||
|
|
||||||
TOY_API void initLiteralDictionary(LiteralDictionary* dictionary);
|
|
||||||
TOY_API void freeLiteralDictionary(LiteralDictionary* dictionary);
|
|
||||||
|
|
||||||
TOY_API void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
|
|
||||||
TOY_API Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
|
||||||
TOY_API void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
|
||||||
|
|
||||||
TOY_API bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
|
||||||
+3
-3
@@ -11,14 +11,14 @@ OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
|||||||
OUTNAME=toy
|
OUTNAME=toy
|
||||||
|
|
||||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
||||||
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||||
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||||
else ifeq ($(shell uname),Linux)
|
else ifeq ($(shell uname),Linux)
|
||||||
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||||
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
|
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
|
||||||
CFLAGS += -fPIC
|
CFLAGS += -fPIC
|
||||||
else ifeq ($(OS),Windows_NT)
|
else ifeq ($(OS),Windows_NT)
|
||||||
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||||
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||||
else ifeq ($(shell uname),Darwin)
|
else ifeq ($(shell uname),Darwin)
|
||||||
LIBLINE = $(OBJ)
|
LIBLINE = $(OBJ)
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
#include "memory.h"
|
|
||||||
#include "refstring.h"
|
|
||||||
|
|
||||||
#include "console_colors.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
//default allocator
|
|
||||||
void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
|
||||||
if (newSize == 0 && oldSize == 0) {
|
|
||||||
//causes issues, so just skip out with a NO-OP
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newSize == 0) {
|
|
||||||
free(pointer);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* mem = realloc(pointer, newSize);
|
|
||||||
|
|
||||||
if (mem == NULL) {
|
|
||||||
fprintf(stderr, ERROR "[internal] Memory allocation error (requested %d for %ld, replacing %d)\n" RESET, (int)newSize, (long int)pointer, (int)oldSize);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static variables
|
|
||||||
static MemoryAllocatorFn allocator;
|
|
||||||
|
|
||||||
//preload
|
|
||||||
static void __attribute__((constructor)) preloadMemoryAllocator() {
|
|
||||||
setMemoryAllocator(defaultMemoryAllocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed API
|
|
||||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
|
||||||
return allocator(pointer, oldSize, newSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMemoryAllocator(MemoryAllocatorFn fn) {
|
|
||||||
if (fn == NULL) {
|
|
||||||
fprintf(stderr, ERROR "[internal] Memory allocator error (can't be null)\n" RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fn == reallocate) {
|
|
||||||
fprintf(stderr, ERROR "[internal] Memory allocator error (can't loop the reallocate function)\n" RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator = fn;
|
|
||||||
setRefStringAllocatorFn(fn);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
|
|
||||||
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
|
|
||||||
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
|
|
||||||
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
|
||||||
#define GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
|
||||||
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
|
||||||
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
|
||||||
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
|
||||||
|
|
||||||
//implementation details
|
|
||||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
|
|
||||||
|
|
||||||
//assign the memory allocator
|
|
||||||
typedef void* (*MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
|
||||||
TOY_API void setMemoryAllocator(MemoryAllocatorFn);
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
typedef enum Opcode {
|
|
||||||
OP_EOF,
|
|
||||||
|
|
||||||
//basic statements
|
|
||||||
OP_ASSERT,
|
|
||||||
OP_PRINT,
|
|
||||||
|
|
||||||
//data
|
|
||||||
OP_LITERAL,
|
|
||||||
OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
|
||||||
OP_LITERAL_RAW, //forcibly get the raw value of the literal
|
|
||||||
|
|
||||||
//arithmetic operators
|
|
||||||
OP_NEGATE,
|
|
||||||
OP_ADDITION,
|
|
||||||
OP_SUBTRACTION,
|
|
||||||
OP_MULTIPLICATION,
|
|
||||||
OP_DIVISION,
|
|
||||||
OP_MODULO,
|
|
||||||
OP_GROUPING_BEGIN,
|
|
||||||
OP_GROUPING_END,
|
|
||||||
|
|
||||||
//variable stuff
|
|
||||||
OP_SCOPE_BEGIN,
|
|
||||||
OP_SCOPE_END,
|
|
||||||
|
|
||||||
OP_TYPE_DECL, //declare a type to be used (as a literal)
|
|
||||||
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
|
||||||
|
|
||||||
OP_VAR_DECL, //declare a variable to be used (as a literal)
|
|
||||||
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
|
||||||
|
|
||||||
OP_FN_DECL, //declare a function to be used (as a literal)
|
|
||||||
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
|
|
||||||
|
|
||||||
OP_VAR_ASSIGN, //assign to a literal
|
|
||||||
OP_VAR_ADDITION_ASSIGN,
|
|
||||||
OP_VAR_SUBTRACTION_ASSIGN,
|
|
||||||
OP_VAR_MULTIPLICATION_ASSIGN,
|
|
||||||
OP_VAR_DIVISION_ASSIGN,
|
|
||||||
OP_VAR_MODULO_ASSIGN,
|
|
||||||
|
|
||||||
OP_TYPE_CAST, //temporarily change a type of an atomic value
|
|
||||||
OP_TYPE_OF, //get the type of a variable
|
|
||||||
|
|
||||||
OP_IMPORT,
|
|
||||||
OP_EXPORT,
|
|
||||||
|
|
||||||
//for indexing
|
|
||||||
OP_INDEX,
|
|
||||||
OP_INDEX_ASSIGN,
|
|
||||||
OP_INDEX_ASSIGN_INTERMEDIATE,
|
|
||||||
OP_DOT,
|
|
||||||
|
|
||||||
//comparison of values
|
|
||||||
OP_COMPARE_EQUAL,
|
|
||||||
OP_COMPARE_NOT_EQUAL,
|
|
||||||
OP_COMPARE_LESS,
|
|
||||||
OP_COMPARE_LESS_EQUAL,
|
|
||||||
OP_COMPARE_GREATER,
|
|
||||||
OP_COMPARE_GREATER_EQUAL,
|
|
||||||
OP_INVERT, //for booleans
|
|
||||||
|
|
||||||
//logical operators
|
|
||||||
OP_AND,
|
|
||||||
OP_OR,
|
|
||||||
|
|
||||||
//jumps, and conditional jumps (absolute)
|
|
||||||
OP_JUMP,
|
|
||||||
OP_IF_FALSE_JUMP,
|
|
||||||
OP_FN_CALL,
|
|
||||||
OP_FN_RETURN,
|
|
||||||
|
|
||||||
//pop the stack at the end of a complex statement
|
|
||||||
OP_POP_STACK,
|
|
||||||
|
|
||||||
//meta
|
|
||||||
OP_FN_END, //different from SECTION_END
|
|
||||||
OP_SECTION_END = 255,
|
|
||||||
//TODO: add more
|
|
||||||
} Opcode;
|
|
||||||
|
|
||||||
-1773
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
|
||||||
#include "lexer.h"
|
|
||||||
#include "ast_node.h"
|
|
||||||
|
|
||||||
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
|
|
||||||
typedef struct {
|
|
||||||
Lexer* lexer;
|
|
||||||
bool error; //I've had an error
|
|
||||||
bool panic; //I am processing an error
|
|
||||||
|
|
||||||
//track the last two outputs from the lexer
|
|
||||||
Token current;
|
|
||||||
Token previous;
|
|
||||||
} Parser;
|
|
||||||
|
|
||||||
TOY_API void initParser(Parser* parser, Lexer* lexer);
|
|
||||||
TOY_API void freeParser(Parser* parser);
|
|
||||||
TOY_API ASTNode* scanParser(Parser* parser);
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#include "refstring.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
//test variable sizes based on platform (safety)
|
|
||||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(RefString) == 12);
|
|
||||||
STATIC_ASSERT(sizeof(int) == 4);
|
|
||||||
STATIC_ASSERT(sizeof(char) == 1);
|
|
||||||
|
|
||||||
//memory allocation
|
|
||||||
static RefStringAllocatorFn allocate;
|
|
||||||
|
|
||||||
void setRefStringAllocatorFn(RefStringAllocatorFn allocator) {
|
|
||||||
allocate = allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
//API
|
|
||||||
RefString* createRefString(char* cstring) {
|
|
||||||
int length = strlen(cstring);
|
|
||||||
|
|
||||||
return createRefStringLength(cstring, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefString* createRefStringLength(char* cstring, int length) {
|
|
||||||
//allocate the memory area (including metadata space)
|
|
||||||
RefString* refString = (RefString*)allocate(NULL, 0, sizeof(int) * 2 + sizeof(char) * length + 1);
|
|
||||||
|
|
||||||
//set the data
|
|
||||||
refString->refcount = 1;
|
|
||||||
refString->length = length;
|
|
||||||
strncpy(refString->data, cstring, refString->length);
|
|
||||||
|
|
||||||
refString->data[refString->length] = '\0'; //string terminator
|
|
||||||
|
|
||||||
return refString;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deleteRefString(RefString* refString) {
|
|
||||||
if (refString->refcount > 0) {
|
|
||||||
//decrement, then check
|
|
||||||
refString->refcount--;
|
|
||||||
if (refString->refcount <= 0) {
|
|
||||||
allocate(refString, sizeof(int) * 2 + sizeof(char) * refString->length + 1, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int countRefString(RefString* refString) {
|
|
||||||
return refString->refcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lengthRefString(RefString* refString) {
|
|
||||||
return refString->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefString* copyRefString(RefString* refString) {
|
|
||||||
//Cheaty McCheater Face
|
|
||||||
refString->refcount++;
|
|
||||||
return refString;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefString* deepCopyRefString(RefString* refString) {
|
|
||||||
//create a new string, with a new refcount
|
|
||||||
return createRefStringLength(refString->data, refString->length);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* toCString(RefString* refString) {
|
|
||||||
return refString->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool equalsRefString(RefString* lhs, RefString* rhs) {
|
|
||||||
//same pointer
|
|
||||||
if (lhs == rhs) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//different length
|
|
||||||
if (lhs->length != rhs->length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//same string
|
|
||||||
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool equalsRefStringCString(RefString* lhs, char* cstring) {
|
|
||||||
//get the rhs length
|
|
||||||
int length = strlen(cstring);
|
|
||||||
|
|
||||||
//different length
|
|
||||||
if (lhs->length != length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//same string
|
|
||||||
return strncmp(lhs->data, cstring, lhs->length) == 0;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
//memory allocation hook
|
|
||||||
typedef void* (*RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
|
||||||
void setRefStringAllocatorFn(RefStringAllocatorFn);
|
|
||||||
|
|
||||||
//the RefString structure
|
|
||||||
typedef struct RefString {
|
|
||||||
int refcount;
|
|
||||||
int length;
|
|
||||||
char data[1];
|
|
||||||
} RefString;
|
|
||||||
|
|
||||||
//API
|
|
||||||
RefString* createRefString(char* cstring);
|
|
||||||
RefString* createRefStringLength(char* cstring, int length);
|
|
||||||
void deleteRefString(RefString* refString);
|
|
||||||
int countRefString(RefString* refString);
|
|
||||||
int lengthRefString(RefString* refString);
|
|
||||||
RefString* copyRefString(RefString* refString);
|
|
||||||
RefString* deepCopyRefString(RefString* refString);
|
|
||||||
char* toCString(RefString* refString);
|
|
||||||
bool equalsRefString(RefString* lhs, RefString* rhs);
|
|
||||||
bool equalsRefStringCString(RefString* lhs, char* cstring);
|
|
||||||
-303
@@ -1,303 +0,0 @@
|
|||||||
#include "scope.h"
|
|
||||||
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
//run up the ancestor chain, freeing anything with 0 references left
|
|
||||||
static void freeAncestorChain(Scope* scope) {
|
|
||||||
scope->references--;
|
|
||||||
|
|
||||||
//free scope chain
|
|
||||||
if (scope->ancestor != NULL) {
|
|
||||||
freeAncestorChain(scope->ancestor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope->references > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
freeLiteralDictionary(&scope->variables);
|
|
||||||
freeLiteralDictionary(&scope->types);
|
|
||||||
|
|
||||||
FREE(Scope, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
//return false if invalid type
|
|
||||||
static bool checkType(Literal typeLiteral, Literal original, Literal value, bool constCheck) {
|
|
||||||
//for constants, fail if original != value
|
|
||||||
if (constCheck && AS_TYPE(typeLiteral).constant && !literalsAreEqual(original, value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//for any types
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//don't allow null types
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//always allow null values
|
|
||||||
if (IS_NULL(value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//for each type, if a mismatch is found, return false
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY && !IS_ARRAY(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_ARRAY(value)) {
|
|
||||||
//check value's type
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if null, assume it's a new entry
|
|
||||||
if (IS_NULL(original)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check children
|
|
||||||
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
|
|
||||||
if (AS_ARRAY(original)->count <= i) {
|
|
||||||
return true; //assume new entry pushed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(original)->literals[i], AS_ARRAY(value)->literals[i], constCheck)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY && !IS_DICTIONARY(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_DICTIONARY(value)) {
|
|
||||||
//check value's type
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if null, assume it's a new entry to a parent
|
|
||||||
if (IS_NULL(original)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check each child of value against the child of original
|
|
||||||
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
|
|
||||||
if (IS_NULL(AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//find the internal child of original that matches this child of value
|
|
||||||
_entry* ptr = NULL;
|
|
||||||
|
|
||||||
for (int j = 0; j < AS_DICTIONARY(original)->capacity; j++) {
|
|
||||||
if (literalsAreEqual(AS_DICTIONARY(original)->entries[j].key, AS_DICTIONARY(value)->entries[i].key)) {
|
|
||||||
ptr = &AS_DICTIONARY(original)->entries[j];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if not found, assume it's a new entry
|
|
||||||
if (!ptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check the type of key and value
|
|
||||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], ptr->key, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], ptr->value, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FUNCTION && !IS_FUNCTION(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
Scope* pushScope(Scope* ancestor) {
|
|
||||||
Scope* scope = ALLOCATE(Scope, 1);
|
|
||||||
scope->ancestor = ancestor;
|
|
||||||
initLiteralDictionary(&scope->variables);
|
|
||||||
initLiteralDictionary(&scope->types);
|
|
||||||
|
|
||||||
//tick up all scope reference counts
|
|
||||||
scope->references = 0;
|
|
||||||
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
|
||||||
ptr->references++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope* popScope(Scope* scope) {
|
|
||||||
if (scope == NULL) { //CAN pop a null
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope* ret = scope->ancestor;
|
|
||||||
|
|
||||||
//BUGFIX: when freeing a scope, free the function's scopes manually
|
|
||||||
for (int i = 0; i < scope->variables.capacity; i++) {
|
|
||||||
//handle keys, just in case
|
|
||||||
if (IS_FUNCTION(scope->variables.entries[i].key)) {
|
|
||||||
popScope(AS_FUNCTION(scope->variables.entries[i].key).scope);
|
|
||||||
AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_FUNCTION(scope->variables.entries[i].value)) {
|
|
||||||
popScope(AS_FUNCTION(scope->variables.entries[i].value).scope);
|
|
||||||
AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeAncestorChain(scope);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope* copyScope(Scope* original) {
|
|
||||||
Scope* scope = ALLOCATE(Scope, 1);
|
|
||||||
scope->ancestor = original->ancestor;
|
|
||||||
initLiteralDictionary(&scope->variables);
|
|
||||||
initLiteralDictionary(&scope->types);
|
|
||||||
|
|
||||||
//tick up all scope reference counts
|
|
||||||
scope->references = 0;
|
|
||||||
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
|
||||||
ptr->references++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//copy the contents of the dictionaries
|
|
||||||
for (int i = 0; i < original->variables.capacity; i++) {
|
|
||||||
if (!IS_NULL(original->variables.entries[i].key)) {
|
|
||||||
setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < original->types.capacity; i++) {
|
|
||||||
if (!IS_NULL(original->types.entries[i].key)) {
|
|
||||||
setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
//returns false if error
|
|
||||||
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
|
|
||||||
//don't redefine a variable within this scope
|
|
||||||
if (existsLiteralDictionary(&scope->variables, key)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//store the type, for later checking on assignment
|
|
||||||
setLiteralDictionary(&scope->types, key, type);
|
|
||||||
|
|
||||||
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
|
|
||||||
if (scope == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
|
||||||
if (!existsLiteralDictionary(&scope->variables, key)) {
|
|
||||||
return isDelcaredScopeVariable(scope->ancestor, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//return false if undefined, or can't be assigned
|
|
||||||
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
|
|
||||||
//dead end
|
|
||||||
if (scope == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
|
||||||
if (!existsLiteralDictionary(&scope->variables, key)) {
|
|
||||||
return setScopeVariable(scope->ancestor, key, value, constCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
//type checking
|
|
||||||
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
|
|
||||||
Literal original = getLiteralDictionary(&scope->variables, key);
|
|
||||||
|
|
||||||
if (!checkType(typeLiteral, original, value, constCheck)) {
|
|
||||||
freeLiteral(typeLiteral);
|
|
||||||
freeLiteral(original);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//actually assign
|
|
||||||
setLiteralDictionary(&scope->variables, key, value);
|
|
||||||
|
|
||||||
freeLiteral(typeLiteral);
|
|
||||||
freeLiteral(original);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
|
|
||||||
//dead end
|
|
||||||
if (scope == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
|
||||||
if (!existsLiteralDictionary(&scope->variables, key)) {
|
|
||||||
return getScopeVariable(scope->ancestor, key, valueHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
*valueHandle = getLiteralDictionary(&scope->variables, key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal getScopeType(Scope* scope, Literal key) {
|
|
||||||
//dead end
|
|
||||||
if (scope == NULL) {
|
|
||||||
return TO_NULL_LITERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if it's not in this scope, keep searching up the chain
|
|
||||||
if (!existsLiteralDictionary(&scope->types, key)) {
|
|
||||||
return getScopeType(scope->ancestor, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return getLiteralDictionary(&scope->types, key);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "literal_array.h"
|
|
||||||
#include "literal_dictionary.h"
|
|
||||||
|
|
||||||
typedef struct Scope {
|
|
||||||
LiteralDictionary variables; //only allow identifiers as the keys
|
|
||||||
LiteralDictionary types; //the types, indexed by identifiers
|
|
||||||
struct Scope* ancestor;
|
|
||||||
int references; //how many scopes point here
|
|
||||||
} Scope;
|
|
||||||
|
|
||||||
Scope* pushScope(Scope* scope);
|
|
||||||
Scope* popScope(Scope* scope);
|
|
||||||
Scope* copyScope(Scope* original);
|
|
||||||
|
|
||||||
//returns false if error
|
|
||||||
bool declareScopeVariable(Scope* scope, Literal key, Literal type);
|
|
||||||
bool isDelcaredScopeVariable(Scope* scope, Literal key);
|
|
||||||
|
|
||||||
//return false if undefined
|
|
||||||
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
|
|
||||||
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
|
|
||||||
|
|
||||||
Literal getScopeType(Scope* scope, Literal key);
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
typedef enum TokenType {
|
|
||||||
//types
|
|
||||||
TOKEN_NULL,
|
|
||||||
TOKEN_BOOLEAN,
|
|
||||||
TOKEN_INTEGER,
|
|
||||||
TOKEN_FLOAT,
|
|
||||||
TOKEN_STRING,
|
|
||||||
TOKEN_ARRAY,
|
|
||||||
TOKEN_DICTIONARY,
|
|
||||||
TOKEN_FUNCTION,
|
|
||||||
TOKEN_OPAQUE,
|
|
||||||
TOKEN_ANY,
|
|
||||||
|
|
||||||
//keywords and reserved words
|
|
||||||
TOKEN_AS,
|
|
||||||
TOKEN_ASSERT,
|
|
||||||
TOKEN_BREAK,
|
|
||||||
TOKEN_CLASS,
|
|
||||||
TOKEN_CONST,
|
|
||||||
TOKEN_CONTINUE,
|
|
||||||
TOKEN_DO,
|
|
||||||
TOKEN_ELSE,
|
|
||||||
TOKEN_EXPORT,
|
|
||||||
TOKEN_FOR,
|
|
||||||
TOKEN_FOREACH,
|
|
||||||
TOKEN_IF,
|
|
||||||
TOKEN_IMPORT,
|
|
||||||
TOKEN_IN,
|
|
||||||
TOKEN_OF,
|
|
||||||
TOKEN_PRINT,
|
|
||||||
TOKEN_RETURN,
|
|
||||||
TOKEN_TYPE,
|
|
||||||
TOKEN_ASTYPE,
|
|
||||||
TOKEN_TYPEOF,
|
|
||||||
TOKEN_VAR,
|
|
||||||
TOKEN_WHILE,
|
|
||||||
|
|
||||||
//literal values
|
|
||||||
TOKEN_IDENTIFIER,
|
|
||||||
TOKEN_LITERAL_TRUE,
|
|
||||||
TOKEN_LITERAL_FALSE,
|
|
||||||
TOKEN_LITERAL_INTEGER,
|
|
||||||
TOKEN_LITERAL_FLOAT,
|
|
||||||
TOKEN_LITERAL_STRING,
|
|
||||||
|
|
||||||
//math operators
|
|
||||||
TOKEN_PLUS,
|
|
||||||
TOKEN_MINUS,
|
|
||||||
TOKEN_MULTIPLY,
|
|
||||||
TOKEN_DIVIDE,
|
|
||||||
TOKEN_MODULO,
|
|
||||||
TOKEN_PLUS_ASSIGN,
|
|
||||||
TOKEN_MINUS_ASSIGN,
|
|
||||||
TOKEN_MULTIPLY_ASSIGN,
|
|
||||||
TOKEN_DIVIDE_ASSIGN,
|
|
||||||
TOKEN_MODULO_ASSIGN,
|
|
||||||
TOKEN_PLUS_PLUS,
|
|
||||||
TOKEN_MINUS_MINUS,
|
|
||||||
TOKEN_ASSIGN,
|
|
||||||
|
|
||||||
//logical operators
|
|
||||||
TOKEN_PAREN_LEFT,
|
|
||||||
TOKEN_PAREN_RIGHT,
|
|
||||||
TOKEN_BRACKET_LEFT,
|
|
||||||
TOKEN_BRACKET_RIGHT,
|
|
||||||
TOKEN_BRACE_LEFT,
|
|
||||||
TOKEN_BRACE_RIGHT,
|
|
||||||
TOKEN_NOT,
|
|
||||||
TOKEN_NOT_EQUAL,
|
|
||||||
TOKEN_EQUAL,
|
|
||||||
TOKEN_LESS,
|
|
||||||
TOKEN_GREATER,
|
|
||||||
TOKEN_LESS_EQUAL,
|
|
||||||
TOKEN_GREATER_EQUAL,
|
|
||||||
TOKEN_AND,
|
|
||||||
TOKEN_OR,
|
|
||||||
|
|
||||||
//other operators
|
|
||||||
TOKEN_COLON,
|
|
||||||
TOKEN_SEMICOLON,
|
|
||||||
TOKEN_COMMA,
|
|
||||||
TOKEN_DOT,
|
|
||||||
TOKEN_PIPE,
|
|
||||||
TOKEN_REST,
|
|
||||||
|
|
||||||
//meta tokens
|
|
||||||
TOKEN_PASS,
|
|
||||||
TOKEN_ERROR,
|
|
||||||
TOKEN_EOF,
|
|
||||||
} TokenType;
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy.h - A Toy Programming Language
|
||||||
|
|
||||||
|
If you're looking how to use Toy directly, try https://toylang.com/
|
||||||
|
Otherwise, this header may help learn how Toy works internally.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Utilities
|
||||||
|
|
||||||
|
These headers define a bunch of useful macros, based on what platform you build for.
|
||||||
|
|
||||||
|
The most important macro is `TOY_API`, which specifies functions intended for the end user.
|
||||||
|
|
||||||
|
* [toy_common.h](toy_common_h.md)
|
||||||
|
* [toy_console_colors.h](toy_console_colors_h.md)
|
||||||
|
* [toy_memory.h](toy_memory_h.md)
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
#include "toy_memory.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Core Pipeline
|
||||||
|
|
||||||
|
From source to execution, each step is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
source -> lexer -> token
|
||||||
|
token -> parser -> AST
|
||||||
|
AST -> compiler -> bytecode
|
||||||
|
bytecode -> interpreter -> result
|
||||||
|
```
|
||||||
|
|
||||||
|
I should note that the parser -> compiler phase is actually made up of two steps - the write step and the collate step. See `Toy_compileString()` in `repl/repl_tools.c` for an example of how to compile properly.
|
||||||
|
|
||||||
|
* [toy_lexer.h](toy_lexer_h.md)
|
||||||
|
* [toy_parser.h](toy_parser_h.md)
|
||||||
|
* [toy_compiler.h](toy_compiler_h.md)
|
||||||
|
* [toy_interpreter.h](toy_interpreter_h.md)
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_lexer.h"
|
||||||
|
#include "toy_parser.h"
|
||||||
|
#include "toy_compiler.h"
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Building Block Structures
|
||||||
|
|
||||||
|
Literals represent any value within the language, including some internal ones that you never see.
|
||||||
|
|
||||||
|
Literal arrays are contiguous arrays within memory, and are the most heavily used structure in Toy.
|
||||||
|
|
||||||
|
Literal dictionaries are unordered key-value hashmaps, that use a running strategy for collisions.
|
||||||
|
|
||||||
|
* [toy_literal.h](toy_literal_h.md)
|
||||||
|
* [toy_literal_array.h](toy_literal_array_h.md)
|
||||||
|
* [toy_literal_dictionary.h](toy_literal_dictionary_h.md)
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_literal.h"
|
||||||
|
#include "toy_literal_array.h"
|
||||||
|
#include "toy_literal_dictionary.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Other Components
|
||||||
|
|
||||||
|
You probably won't use these directly, but they're a good learning opportunity.
|
||||||
|
|
||||||
|
`Toy_Scope` holds the variables of a specific scope within Toy - be it a script, a function, a block, etc. Scopes are also where the type system lives at runtime. They use identifier literals as keys, exclusively.
|
||||||
|
|
||||||
|
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and faster to copy and move. In reality, since strings are considered immutable, multiple variables can point to the same string to save memory, and you can just create a new one of these vars pointing to the original rather than copying entirely for a speed boost. This module has it's own memory allocator system that is plugged into the main memory allocator.
|
||||||
|
|
||||||
|
`Toy_RefFunction` acts similarly to `Toy_RefString`, but instead operates on function bytecode.
|
||||||
|
|
||||||
|
* [toy_scope.h](toy_scope_h.md)
|
||||||
|
* [toy_refstring.h](toy_refstring_h.md)
|
||||||
|
* [toy_reffunction.h](toy_reffunction_h.md)
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_scope.h"
|
||||||
|
#include "toy_refstring.h"
|
||||||
|
#include "toy_reffunction.h"
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
#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_AND:
|
||||||
|
Toy_freeASTNode(node->pathAnd.left);
|
||||||
|
Toy_freeASTNode(node->pathAnd.right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOY_AST_NODE_OR:
|
||||||
|
Toy_freeASTNode(node->pathOr.left);
|
||||||
|
Toy_freeASTNode(node->pathOr.right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOY_AST_NODE_PREFIX_INCREMENT:
|
||||||
|
Toy_freeLiteral(node->prefixIncrement.identifier);
|
||||||
|
break;
|
||||||
|
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_emitASTNodeAnd(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs) {
|
||||||
|
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_NODE_AND;
|
||||||
|
tmp->pathAnd.left = *nodeHandle;
|
||||||
|
tmp->pathAnd.right = rhs;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_emitASTNodeOr(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs) {
|
||||||
|
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_NODE_OR;
|
||||||
|
tmp->pathOr.left = *nodeHandle;
|
||||||
|
tmp->pathOr.right = rhs;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||||
|
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,296 @@
|
|||||||
|
#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_AND, //for control flow
|
||||||
|
TOY_AST_NODE_OR, //for control flow
|
||||||
|
TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable
|
||||||
|
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||||
|
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||||
|
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;
|
||||||
|
|
||||||
|
//and operator
|
||||||
|
void Toy_emitASTNodeAnd(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs); //handled node becomes lhs
|
||||||
|
|
||||||
|
typedef struct Toy_NodeAnd {
|
||||||
|
Toy_ASTNodeType type;
|
||||||
|
Toy_ASTNode* left;
|
||||||
|
Toy_ASTNode* right;
|
||||||
|
} Toy_NodeAnd;
|
||||||
|
|
||||||
|
//or operator
|
||||||
|
void Toy_emitASTNodeOr(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs); //handled node becomes lhs
|
||||||
|
|
||||||
|
typedef struct Toy_NodeOr {
|
||||||
|
Toy_ASTNodeType type;
|
||||||
|
Toy_ASTNode* left;
|
||||||
|
Toy_ASTNode* right;
|
||||||
|
} Toy_NodeOr;
|
||||||
|
|
||||||
|
//pre-post increment/decrement
|
||||||
|
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||||
|
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||||
|
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_NodeAnd pathAnd;
|
||||||
|
Toy_NodeOr pathOr;
|
||||||
|
Toy_NodePrefixIncrement prefixIncrement;
|
||||||
|
Toy_NodePrefixDecrement prefixDecrement;
|
||||||
|
Toy_NodePostfixIncrement postfixIncrement;
|
||||||
|
Toy_NodePostfixDecrement postfixDecrement;
|
||||||
|
Toy_NodeImport import;
|
||||||
|
};
|
||||||
|
|
||||||
|
//see toy_parser.h for more documentation on this function
|
||||||
|
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_interpreter.h"
|
||||||
|
|
||||||
|
//the _index function is a historical oddity - it's used whenever a compound is indexed
|
||||||
|
int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
|
|
||||||
|
//globally available native functions
|
||||||
|
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
|
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
|
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
|
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
|
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
|
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||||
+77
-48
@@ -4,7 +4,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
//test variable sizes based on platform
|
//test variable sizes based on platform - see issue #35
|
||||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(char) == 1);
|
STATIC_ASSERT(sizeof(char) == 1);
|
||||||
@@ -15,77 +15,106 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
|
|||||||
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
||||||
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
static const char* build = __DATE__ " " __TIME__;
|
||||||
|
|
||||||
//declare the singleton
|
const char* Toy_private_version_build() {
|
||||||
Command command;
|
return build;
|
||||||
|
}
|
||||||
|
|
||||||
void initCommand(int argc, const char* argv[]) {
|
//declare the singleton with default values
|
||||||
//default values
|
Toy_CommandLine Toy_commandLine = {
|
||||||
command.error = false;
|
.error = false,
|
||||||
command.help = false;
|
.help = false,
|
||||||
command.version = false;
|
.version = false,
|
||||||
command.binaryfile = NULL;
|
.binaryfile = NULL,
|
||||||
command.sourcefile = NULL;
|
.sourcefile = NULL,
|
||||||
command.compilefile = NULL;
|
.compilefile = NULL,
|
||||||
command.outfile = "out.tb";
|
.outfile = "out.tb",
|
||||||
command.source = NULL;
|
.source = NULL,
|
||||||
command.verbose = false;
|
.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
|
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||||
command.error = true; //error state by default, set to false by successful flags
|
Toy_commandLine.error = true; //error state by default, set to false by successful flags
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||||
command.help = true;
|
Toy_commandLine.help = true;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||||
command.version = true;
|
Toy_commandLine.version = true;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
||||||
command.verbose = true;
|
Toy_commandLine.verbose = true;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
||||||
command.sourcefile = (char*)argv[i + 1];
|
Toy_commandLine.sourcefile = (char*)argv[i + 1];
|
||||||
i++;
|
i++;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
||||||
command.source = (char*)argv[i + 1];
|
Toy_commandLine.source = (char*)argv[i + 1];
|
||||||
i++;
|
i++;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
||||||
command.compilefile = (char*)argv[i + 1];
|
Toy_commandLine.compilefile = (char*)argv[i + 1];
|
||||||
i++;
|
i++;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
||||||
command.outfile = (char*)argv[i + 1];
|
Toy_commandLine.outfile = (char*)argv[i + 1];
|
||||||
i++;
|
i++;
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!strcmp(argv[i], "-t") || !strcmp(argv[i], "--initial")) && i + 1 < argc) {
|
||||||
|
Toy_commandLine.initialfile = (char*)argv[i + 1];
|
||||||
|
i++;
|
||||||
|
Toy_commandLine.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[i], "-p")) {
|
||||||
|
Toy_commandLine.parseBytecodeHeader = true;
|
||||||
|
|
||||||
|
if (Toy_commandLine.binaryfile) {
|
||||||
|
Toy_commandLine.error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[i], "-n")) {
|
||||||
|
Toy_commandLine.enablePrintNewline = false;
|
||||||
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//option without a flag + ending in .tb = binary input
|
//option without a flag + ending in .tb = binary input
|
||||||
if (i < argc) {
|
if (i < argc) {
|
||||||
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
||||||
command.binaryfile = (char*)argv[i];
|
Toy_commandLine.binaryfile = (char*)argv[i];
|
||||||
command.error = false;
|
Toy_commandLine.error = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,31 +124,31 @@ void initCommand(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usageCommand(int argc, const char* argv[]) {
|
void Toy_usageCommandLine(int argc, const char* argv[]) {
|
||||||
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
|
printf("Usage: %s [ file.tb | -h | -v | -d | -f file.toy | -i source | -c file.toy -o out.tb | -t file.toy ]\n\n", argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void helpCommand(int argc, const char* argv[]) {
|
void Toy_helpCommandLine(int argc, const char* argv[]) {
|
||||||
usageCommand(argc, argv);
|
Toy_usageCommandLine(argc, argv);
|
||||||
|
|
||||||
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
printf(" -h, --help\t\t\tShow this help then exit.\n");
|
||||||
printf("-h\t| --help\t\tShow this help then exit.\n\n");
|
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
|
||||||
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
|
printf(" -d, --debug\t\t\tBe verbose when operating.\n");
|
||||||
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
|
printf(" -f, --file filename\t\tParse, compile and execute the source file.\n");
|
||||||
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
|
printf(" -i, --input source\t\tParse, compile and execute this given string of source code.\n");
|
||||||
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
|
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
|
||||||
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
|
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
|
||||||
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
|
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
|
||||||
|
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
|
||||||
|
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyrightCommand(int argc, const char* argv[]) {
|
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
|
||||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||||
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
|
printf("Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios\n\n");
|
||||||
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
|
printf("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("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("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("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");
|
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
+82
-27
@@ -1,34 +1,89 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_common.h
|
||||||
|
|
||||||
|
This file is generally included in most header files within Toy, as it is where the TOY_API macro is defined. It also has some utilities intended for use only by the repl.
|
||||||
|
|
||||||
|
## Defined Macros
|
||||||
|
!*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define TOY_VERSION_MAJOR 0
|
/*!
|
||||||
#define TOY_VERSION_MINOR 6
|
### TOY_API
|
||||||
#define TOY_VERSION_PATCH 4
|
|
||||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
This definition of this macro is platform-dependant, and used to enable cross-platform compilation of shared and static libraries.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||||
|
|
||||||
//platform exports/imports
|
|
||||||
#if defined(__linux__)
|
|
||||||
#define TOY_API extern
|
#define TOY_API extern
|
||||||
#include <time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#elif defined(_WIN32) || defined(WIN32)
|
#elif defined(_MSC_VER)
|
||||||
#define TOY_API
|
|
||||||
#include <time.h>
|
#ifndef TOY_EXPORT
|
||||||
#include <sys/time.h>
|
#define TOY_API __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
#define TOY_API __declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define TOY_API
|
|
||||||
#include <time.h>
|
#define TOY_API extern
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TOY_EXPORT
|
/*!
|
||||||
//for processing the command line arguments
|
### TOY_VERSION_MAJOR
|
||||||
|
|
||||||
|
The current major version of Toy. This value is embedded into the bytecode, and the interpreter will refuse to run bytecode with a major version that does not match it’s own version.
|
||||||
|
|
||||||
|
This value MUST fit into an unsigned char.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_VERSION_MAJOR 1
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_VERSION_MINOR
|
||||||
|
|
||||||
|
The current minor version of Toy. This value is embedded into the bytecode, and the interpreter will refuse to run bytecode with a minor version that is greater than its own minor version.
|
||||||
|
|
||||||
|
This value MUST fit into an unsigned char.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_VERSION_MINOR 3
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_VERSION_PATCH
|
||||||
|
|
||||||
|
The current patch version of Toy. This value is embedded into the bytecode.
|
||||||
|
|
||||||
|
This value MUST fit into an unsigned char.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_VERSION_PATCH 2
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_VERSION_BUILD
|
||||||
|
|
||||||
|
The current build version of Toy. This value is embedded into the bytecode.
|
||||||
|
|
||||||
|
This evaluates to a c-string, which contains build information such as compilation date and time of the interpreter. When in verbose mode, the compiler will display a warning if the build version of the bytecode does not match the build version of the interpreter.
|
||||||
|
|
||||||
|
This macro may also be used to store additonal information about forks of the Toy codebase.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_VERSION_BUILD Toy_private_version_build()
|
||||||
|
TOY_API const char* Toy_private_version_build();
|
||||||
|
|
||||||
|
/*
|
||||||
|
The following code is intended only for use within the repl.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//for processing the command line arguments in the repl
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool error;
|
bool error;
|
||||||
bool help;
|
bool help;
|
||||||
@@ -38,17 +93,17 @@ typedef struct {
|
|||||||
char* compilefile;
|
char* compilefile;
|
||||||
char* outfile; //defaults to out.tb
|
char* outfile; //defaults to out.tb
|
||||||
char* source;
|
char* source;
|
||||||
|
char* initialfile;
|
||||||
|
bool enablePrintNewline;
|
||||||
|
bool parseBytecodeHeader;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
} Command;
|
} Toy_CommandLine;
|
||||||
|
|
||||||
extern Command command;
|
//these are intended for the repl only, despite using the api prefix
|
||||||
|
TOY_API Toy_CommandLine Toy_commandLine;
|
||||||
|
|
||||||
void initCommand(int argc, const char* argv[]);
|
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
|
||||||
|
|
||||||
void usageCommand(int argc, const char* argv[]);
|
TOY_API void Toy_usageCommandLine(int argc, const char* argv[]);
|
||||||
void helpCommand(int argc, const char* argv[]);
|
TOY_API void Toy_helpCommandLine(int argc, const char* argv[]);
|
||||||
void copyrightCommand(int argc, const char* argv[]);
|
TOY_API void Toy_copyrightCommandLine(int argc, const char* argv[]);
|
||||||
#endif
|
|
||||||
|
|
||||||
//NOTE: assigning to a byte from a short loses data
|
|
||||||
#define AS_USHORT(value) (*(unsigned short*)(&(value)))
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_compiler.h
|
||||||
|
|
||||||
|
This header defines the compiler structure, which is used to transform abstract syntax trees into usable intermediate bytecode. There are two steps to generating bytecode - the writing step, and the collation step.
|
||||||
|
|
||||||
|
During the writing step, the core of the program is generated, along with a series of literals representing the values within the program; these values are compressed and flattened into semi-unrecognizable forms. If the same literal is used multiple times in a program, such as a variable name, the name itself is replaced by a reference to the flattened literals within the cache.
|
||||||
|
|
||||||
|
During the collation step, everything from the core program’s execution instructions, the flattened literals, the functions (which have their own sections and protocols within the bytecode) and version information (such as the macros defined in toy_common.h) are all combined into a single buffer of bytes, known as bytecode. This bytecode can then be safely saved to a file or immediately executed.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_opcodes.h"
|
||||||
|
#include "toy_ast_node.h"
|
||||||
|
#include "toy_literal_array.h"
|
||||||
|
|
||||||
|
typedef struct Toy_Compiler {
|
||||||
|
Toy_LiteralArray literalCache;
|
||||||
|
unsigned char* bytecode;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
bool panic;
|
||||||
|
} Toy_Compiler;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Define Functions
|
||||||
|
|
||||||
|
Executing the following functions out-of-order causes undefiend behaviour.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_initCompiler(Toy_Compiler* compiler)
|
||||||
|
|
||||||
|
This function initializes the given compiler.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node)
|
||||||
|
|
||||||
|
This function writes the given `node` argument to the compiler. During the writing step, this function may be called repeatedly, with a stream of results from `Toy_scanParser()`, until `Toy_scanParser()` returns `NULL`.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size)
|
||||||
|
|
||||||
|
This function returns a buffer of bytes, known as "bytecode", created from the given compiler; it also stores the size of the bytecode in the variable pointed to by `size`.
|
||||||
|
|
||||||
|
Calling `Toy_collateCompiler()` multiple times on the same compiler will produce undefined behaviour.
|
||||||
|
!*/
|
||||||
|
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeCompiler(Toy_Compiler* compiler)
|
||||||
|
|
||||||
|
This function frees a compiler. Calling this on a compiler which has not been collated will free that compiler as expected - anything written to it will be lost.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
|
||||||
@@ -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
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,200 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_interpreter.h
|
||||||
|
|
||||||
|
This header defines the interpreter structure, which is the beating heart of Toy.
|
||||||
|
|
||||||
|
`Toy_Interpreter` is a stack-based, bytecode-driven interpreter with a number of customisation options, including "hooks"; native C functions wrapped in `Toy_Literal` instances, injected into the interpreter in order to give the Toy scripts access to libraries via the `import` keyword. The hooks, when invoked this way, can then inject further native functions into the interpreter's current scope. Exactly which hooks are made available varies by host program, but `standard` is the most commonly included one.
|
||||||
|
|
||||||
|
Another useful customisation feature is the ability to redicrect output from the `print` and `assert` keywords, as well as any internal errors that occur. This can allow you to add in a logging system, or even hook the `print` statement up to some kind of HUD.
|
||||||
|
|
||||||
|
## Defined Interfaces
|
||||||
|
|
||||||
|
Note: These interfaces are *actually* defined in [toy_literal.h](toy_literal_h.md) but are documented here, because this is where it matters most.
|
||||||
|
|
||||||
|
### typedef void (*Toy_PrintFn)(const char*)
|
||||||
|
|
||||||
|
This is the interface used by "print functions" - that is, functions used to print messages from the `print` and `assert` keywords, as well as internal interpreter errors.
|
||||||
|
|
||||||
|
### typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments)
|
||||||
|
|
||||||
|
This is the interface used by "native functions" - that is, functions written in C which can be called directly by Toy scripts.
|
||||||
|
|
||||||
|
The arguments to the function are passed in as a `Toy_LiteralArray`.
|
||||||
|
|
||||||
|
### typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias)
|
||||||
|
|
||||||
|
This is the interface used by "hook functions" - that is, functions written in C which are invoked by using the `import` keyword, and are intended to inject other native functions into the current scope. While hook functions are capable of doing other things, this is greatly discouraged.
|
||||||
|
|
||||||
|
The identifier of the library (its name) is passed in as a `Toy_Literal`, as is any given alias; if no alias is given, then `alias` will be a null literal. Here, the identifier is `standard`, while the alias is `std`.
|
||||||
|
|
||||||
|
```
|
||||||
|
import standard as std;
|
||||||
|
```
|
||||||
|
|
||||||
|
Conventionally, when an alias is given, all of the functions should instead be inserted into a `Toy_LiteralDictionary` which is then inserted into the scope with the alias as its identifier.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_literal.h"
|
||||||
|
#include "toy_literal_array.h"
|
||||||
|
#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;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_initInterpreter(Toy_Interpreter* interpreter)
|
||||||
|
|
||||||
|
This function initializes the interpreter. It allocates memory for internal systems such as the stack, and zeroes-out systems that have yet to be invoked. Internally, it also invokes `Toy_resetInterpreter` to initialize the environment.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length)
|
||||||
|
|
||||||
|
This function takes a `Toy_Interpreter` and `bytecode` (as well as the `length` of the bytecode), checks its version information, parses and un-flattens the literal cache, and executes the compiled program stored in the bytecode. This function also consumes the bytecode, so the `bytecode` argument is no longer valid after calls.
|
||||||
|
|
||||||
|
If the given bytecode's embedded version is not compatible with the current interpreter, then this function will refuse to execute.
|
||||||
|
|
||||||
|
Re-using a `Toy_Interpreter` instance without first resetting it is possible (that's how the repl works), however doing so may have unintended consequences if the scripts are not intended to be used in such a way. Any variables declared will persist.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, size_t length);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_resetInterpreter(Toy_Interpreter* interpreter)
|
||||||
|
|
||||||
|
This function frees any scopes that the scripts have built up, and generates a new one. It also injects several globally available functions:
|
||||||
|
|
||||||
|
* set
|
||||||
|
* get
|
||||||
|
* push
|
||||||
|
* pop
|
||||||
|
* length
|
||||||
|
* clear
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeInterpreter(Toy_Interpreter* interpreter)
|
||||||
|
|
||||||
|
This function frees a `Toy_Interpreter`, clearing all of the memory used within. That interpreter is no longer valid for use, and must be re-initialized.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func)
|
||||||
|
|
||||||
|
This function will inject the given native function `func` into the `Toy_Interpreter`'s current scope, with the identifer as `name`. Both the name and function will be converted into literals internally before being stored. It will return true on success, otherwise it will return false.
|
||||||
|
|
||||||
|
The primary use of this function is within hooks.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook)
|
||||||
|
|
||||||
|
This function will inject the given native function `hook` into the `Toy_Interpreter`'s hook cache, with the identifier as `name`. Both the name and the function will be converted into literals internally before being stored. It will return true on success, otherwise it will return false.
|
||||||
|
|
||||||
|
Hooks are invoked with the `import` keyword within Toy's scripts.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns)
|
||||||
|
|
||||||
|
This function calls a `Toy_Literal` which contains a function, with the arguments to that function passed in as `arguments` and the results stored in `returns`. It returns true on success, otherwise it returns false.
|
||||||
|
|
||||||
|
The literal `func` can be either a native function or a Toy function, but it won't execute a hook.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns)
|
||||||
|
|
||||||
|
This utility function will find a `Toy_literal` within the `Toy_Interpreter`'s scope with an identifier that matches `name`, and will invoke it using `Toy_callLiteralFn` (passing in `arguments` and `returns` as expected).
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr)
|
||||||
|
|
||||||
|
Sometimes, native functions will receive `Toy_Literal` identifiers instead of the values - the correct values can be retreived from the given interpreter's scope using the following pattern:
|
||||||
|
|
||||||
|
```c
|
||||||
|
Toy_Literal foobarIdn = foobar;
|
||||||
|
if (TOY_IS_IDENTIFIER(foobar) && Toy_parseIdentifierToValue(interpreter, &foobar)) {
|
||||||
|
freeLiteral(foobarIdn); //remember to free the identifier
|
||||||
|
}
|
||||||
|
```
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput)
|
||||||
|
|
||||||
|
This function sets the function called by the `print` keyword. By default, the following wrapper is used:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static void printWrapper(const char* output) {
|
||||||
|
printf("%s\n", output);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: The above is a very minor lie - in reality there are some preprocessor directives to allow the repl's `-n` flag to work.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput)
|
||||||
|
|
||||||
|
This function sets the function called by the `assert` keyword on failure. By default, the following wrapper is used:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static void assertWrapper(const char* output) {
|
||||||
|
fprintf(stderr, "Assertion failure: %s\n", output);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput)
|
||||||
|
|
||||||
|
This function sets the function called when an error occurs within the interpreter. By default, the following wrapper is used:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static void errorWrapper(const char* output) {
|
||||||
|
fprintf(stderr, "%s", output); //no newline
|
||||||
|
}
|
||||||
|
```
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
|
||||||
@@ -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) == (size_t)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||||
|
Toy_Token token;
|
||||||
|
|
||||||
|
token.type = Toy_keywordTypes[i].type;
|
||||||
|
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_AND);
|
||||||
|
}
|
||||||
|
|
||||||
|
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR_OR : TOY_TOKEN_PIPE);
|
||||||
|
|
||||||
|
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
|
||||||
|
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
|
||||||
|
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,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_lexer.h
|
||||||
|
|
||||||
|
This header defines the lexer and token structures, which can be bound to a piece of source code, and used to tokenize it within a parser.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_token_types.h"
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_initLexer(Toy_Lexer* lexer, const char* source)
|
||||||
|
|
||||||
|
This function initializes a lexer, binding it to the `source` parameter; the lexer is now ready to be passed to the parser.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer)
|
||||||
|
|
||||||
|
This function "scans" the lexer, returning a token to the parser.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_private_printToken(Toy_Token* token)
|
||||||
|
|
||||||
|
This function prints a given token to stdout.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_private_printToken(Toy_Token* token);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_private_setComments(Toy_Lexer* lexer, bool enabled)
|
||||||
|
|
||||||
|
This function sets whether comments are allowed within source code. By default, comments are allowed, and are only disabled in the repl.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total = snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str ? str : "\0");
|
||||||
|
globalPrintCount += total;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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,380 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_literal.h
|
||||||
|
|
||||||
|
This header defines the literal structure, which is used extensively throughout Toy to represent values of some kind.
|
||||||
|
|
||||||
|
The main way of interacting with literals is to use a macro of some kind, as the exact implementation of `Toy_Literal` has and will change based on the needs of Toy.
|
||||||
|
|
||||||
|
User data can be passed around within Toy as an opaque type - use the tag value for determining what kind of opaque it is, or leave it as 0.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_refstring.h"
|
||||||
|
#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*);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Enums
|
||||||
|
|
||||||
|
### Toy_LiteralType
|
||||||
|
|
||||||
|
* `TOY_LITERAL_NULL`
|
||||||
|
* `TOY_LITERAL_BOOLEAN`
|
||||||
|
* `TOY_LITERAL_INTEGER`
|
||||||
|
* `TOY_LITERAL_FLOAT`
|
||||||
|
* `TOY_LITERAL_STRING`
|
||||||
|
* `TOY_LITERAL_ARRAY`
|
||||||
|
* `TOY_LITERAL_DICTIONARY`
|
||||||
|
* `TOY_LITERAL_FUNCTION`
|
||||||
|
* `TOY_LITERAL_FUNCTION_NATIVE`
|
||||||
|
* `TOY_LITERAL_FUNCTION_HOOK`
|
||||||
|
* `TOY_LITERAL_IDENTIFIER`
|
||||||
|
* `TOY_LITERAL_TYPE`
|
||||||
|
* `TOY_LITERAL_OPAQUE`
|
||||||
|
* `TOY_LITERAL_ANY`
|
||||||
|
|
||||||
|
These are the main values of `Toy_LiteralType`, each of which represents a potential state of the `Toy_Literal` structure. Do not interact with a literal without determining its type with the `IS_*` macros first.
|
||||||
|
|
||||||
|
Other type values are possible, but are only used internally.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TOY_LITERAL_NULL,
|
||||||
|
TOY_LITERAL_BOOLEAN,
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Macros
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The following macros are used to determine if a given literal, passed in as `value`, is of a specific type. It should be noted that `TOY_IS_FUNCTION` will return false for native and hook functions.
|
||||||
|
|
||||||
|
* `TOY_IS_NULL(value)`
|
||||||
|
* `TOY_IS_BOOLEAN(value)`
|
||||||
|
* `TOY_IS_INTEGER(value)`
|
||||||
|
* `TOY_IS_FLOAT(value)`
|
||||||
|
* `TOY_IS_STRING(value)`
|
||||||
|
* `TOY_IS_ARRAY(value)`
|
||||||
|
* `TOY_IS_DICTIONARY(value)`
|
||||||
|
* `TOY_IS_FUNCTION(value)`
|
||||||
|
* `TOY_IS_FUNCTION_NATIVE(value)`
|
||||||
|
* `TOY_IS_FUNCTION_HOOK(value)`
|
||||||
|
* `TOY_IS_IDENTIFIER(value)`
|
||||||
|
* `TOY_IS_TYPE(value)`
|
||||||
|
* `TOY_IS_OPAQUE(value)`
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
|
||||||
|
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
||||||
|
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
||||||
|
#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)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The following macros are used to cast a literal to a specific C type to be used.
|
||||||
|
|
||||||
|
* `TOY_AS_BOOLEAN(value)`
|
||||||
|
* `TOY_AS_INTEGER(value)`
|
||||||
|
* `TOY_AS_FLOAT(value)`
|
||||||
|
* `TOY_AS_STRING(value)`
|
||||||
|
* `TOY_AS_ARRAY(value)`
|
||||||
|
* `TOY_AS_DICTIONARY(value)`
|
||||||
|
* `TOY_AS_FUNCTION(value)`
|
||||||
|
* `TOY_AS_FUNCTION_NATIVE(value)`
|
||||||
|
* `TOY_AS_FUNCTION_HOOK(value)`
|
||||||
|
* `TOY_AS_IDENTIFIER(value)`
|
||||||
|
* `TOY_AS_TYPE(value)`
|
||||||
|
* `TOY_AS_OPAQUE(value)`
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
|
||||||
|
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
||||||
|
#define TOY_AS_FLOAT(value) ((value).as.number)
|
||||||
|
#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)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The following macros are used to create a new literal, with the given `value` as it's internal value.
|
||||||
|
|
||||||
|
* `TOY_TO_NULL_LITERAL` - does not need parantheses
|
||||||
|
* `TOY_TO_BOOLEAN_LITERAL(value)`
|
||||||
|
* `TOY_TO_INTEGER_LITERAL(value)`
|
||||||
|
* `TOY_TO_FLOAT_LITERAL(value)`
|
||||||
|
* `TOY_TO_STRING_LITERAL(value)`
|
||||||
|
* `TOY_TO_ARRAY_LITERAL(value)`
|
||||||
|
* `TOY_TO_DICTIONARY_LITERAL(value)`
|
||||||
|
* `TOY_TO_FUNCTION_LITERAL(value, l)` - `l` represents the length of the bytecode passed as `value`
|
||||||
|
* `TOY_TO_FUNCTION_NATIVE_LITERAL(value)`
|
||||||
|
* `TOY_TO_FUNCTION_HOOK_LITERAL(value)`
|
||||||
|
* `TOY_TO_IDENTIFIER_LITERAL(value)`
|
||||||
|
* `TOY_TO_TYPE_LITERAL(value, c)` - `c` is the true of the type should be const
|
||||||
|
* `TOY_TO_OPAQUE_LITERAL(value, t)` - `t` is the integer tag
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_TO_NULL_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_NULL})
|
||||||
|
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){{ .boolean = value }, TOY_LITERAL_BOOLEAN})
|
||||||
|
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){{ .integer = value }, TOY_LITERAL_INTEGER})
|
||||||
|
#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 - not for general use
|
||||||
|
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
||||||
|
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## More Defined Macros
|
||||||
|
|
||||||
|
The following macros are utilities used throughout Toy's internals, and are available for the user as well.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_IS_TRUTHY(x)
|
||||||
|
|
||||||
|
Returns true of the literal `x` is truthy, otherwise it returns false.
|
||||||
|
|
||||||
|
Currently, every value is considered truthy except `false`, which is falsy and `null`, which is neither true or false.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_AS_FUNCTION_BYTECODE_LENGTH(lit)
|
||||||
|
|
||||||
|
Returns the length of a Toy function's bytecode.
|
||||||
|
|
||||||
|
This macro is only valid on `TOY_LITERAL_FUNCTION`.
|
||||||
|
!*/
|
||||||
|
#define TOY_AS_FUNCTION_BYTECODE_LENGTH(lit) (Toy_lengthRefFunction((lit).inner.ptr))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_MAX_STRING_LENGTH
|
||||||
|
|
||||||
|
The maximum length of a string in Toy, which is 4096 bytes by default. This can be changed at compile time, but the results of doing so are not officially supported.
|
||||||
|
!*/
|
||||||
|
#define TOY_MAX_STRING_LENGTH 4096
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_HASH_I(lit)
|
||||||
|
|
||||||
|
Identifiers are the names of values within Toy; to speed up execution, their "hash value" is computed at compile time and stored within them. Use this to access it, if needed.
|
||||||
|
|
||||||
|
This macro is only valid on `TOY_LITERAL_IDENTIFIER`.
|
||||||
|
!*/
|
||||||
|
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_TYPE_PUSH_SUBTYPE(lit, subtype)
|
||||||
|
|
||||||
|
When building a complex type, such as the type of an array or dictionary, you may need to specify inner types. Use this to push a `subtype`. calling `Toy_freeLiteral()` on the outermost type should clean up all inner types, as expected.
|
||||||
|
|
||||||
|
This macro returns the index of the newly pushed value within it's parent.
|
||||||
|
|
||||||
|
This macro is only valid on `TOY_LITERAL_TYPE`, for both `type` and `subtype`.
|
||||||
|
!*/
|
||||||
|
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_GET_OPAQUE_TAG(o)
|
||||||
|
|
||||||
|
Returns the value of the opaque `o`'s tag.
|
||||||
|
|
||||||
|
This macro is only valid on `TOY_LITERAL_OPAQUE`.
|
||||||
|
!*/
|
||||||
|
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeLiteral(Toy_Literal literal)
|
||||||
|
|
||||||
|
This function frees the given literal's memory. Any internal pointers are now invalid.
|
||||||
|
|
||||||
|
This function should be called on EVERY literal when it is no longer needed, regardless of type.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_Literal Toy_copyLiteral(Toy_Literal original)
|
||||||
|
|
||||||
|
This function returns a copy of the given literal. Literals should never be copied without this function, as it handles a lot of internal memory allocations.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs)
|
||||||
|
|
||||||
|
This checks to see if two given literals are equal.
|
||||||
|
|
||||||
|
When an integer and a float are compared, the integer is cooerced into a float for the duration of the call.
|
||||||
|
|
||||||
|
Arrays or dictionaries are equal only if their keys and values all equal. Likewise, types only equal if all subtypes are equal, in order.
|
||||||
|
|
||||||
|
Functions and opaques are never equal to anything, while values with the type `TOY_LITERAL_ANY` are always equal.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### int Toy_hashLiteral(Toy_Literal lit)
|
||||||
|
|
||||||
|
This finds the hash of a literal, for various purposes. Different hashing algorithms are used for different types, and some types can't be hashed at all.
|
||||||
|
|
||||||
|
types that can't be hashed are
|
||||||
|
|
||||||
|
* all kinds of functions
|
||||||
|
* type
|
||||||
|
* opaque
|
||||||
|
* any
|
||||||
|
|
||||||
|
In the case of identifiers, their hashes are precomputed on creation and are stored within the literal.
|
||||||
|
!*/
|
||||||
|
TOY_API int Toy_hashLiteral(Toy_Literal lit);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_printLiteral(Toy_Literal literal)
|
||||||
|
|
||||||
|
This wraps a call to `Toy_printLiteralCustom`, with a printf-stdout wrapper as `printFn`.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_printLiteral(Toy_Literal literal);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_printLiteralCustom(Toy_Literal literal, PrintFn printFn)
|
||||||
|
|
||||||
|
This function passes the string representation of `literal` to `printFn`.
|
||||||
|
|
||||||
|
This function is not thread safe - due to the loopy and recursive nature of printing compound values, this function uses some globally persistent variables.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_private_isTruthy(Toy_Literal x)
|
||||||
|
|
||||||
|
Utilized by the `TOY_IS_TRUTHY` macro.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_private_toIdentifierLiteral(Toy_RefString* ptr)
|
||||||
|
|
||||||
|
Utilized by the `TOY_TO_IDENTIFIER_LITERAL` macro.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype)
|
||||||
|
|
||||||
|
Utilized by the `TOY_TYPE_PUSH_SUBTYPE` macro.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
|
||||||
@@ -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_private_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||||
|
for (int i = 0; i < array->count; i++) {
|
||||||
|
//not the same type
|
||||||
|
if (array->literals[i].type != literal.type) {
|
||||||
|
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,82 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# literal_array.h
|
||||||
|
|
||||||
|
This header defines the array structure, which manages a series of `Toy_Literal` instances in sequential memory. The array does not take ownership of given literals, instead it makes an internal copy.
|
||||||
|
|
||||||
|
The array type is one of two fundemental data structures used throughout Toy - the other is the dictionary.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_literal.h"
|
||||||
|
|
||||||
|
typedef struct Toy_LiteralArray {
|
||||||
|
Toy_Literal* literals;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} Toy_LiteralArray;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
### void Toy_initLiteralArray(Toy_LiteralArray* array)
|
||||||
|
|
||||||
|
This function initializes a `Toy_LiteralArray` pointed to by `array`.
|
||||||
|
*/
|
||||||
|
TOY_API void Toy_initLiteralArray(Toy_LiteralArray* array);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeLiteralArray(Toy_LiteralArray* array)
|
||||||
|
|
||||||
|
This function frees a `Toy_LiteralArray` pointed to by `array`. Every literal within is passed to `Toy_freeLiteral()` before its memory is released.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeLiteralArray(Toy_LiteralArray* array);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal)
|
||||||
|
|
||||||
|
This function adds a new `literal` to the end of the `array`, growing the array's internal buffer if needed.
|
||||||
|
|
||||||
|
This function returns the index of the inserted value.
|
||||||
|
!*/
|
||||||
|
TOY_API int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array)
|
||||||
|
|
||||||
|
This function removes the literal at the end of the `array`, and returns it.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value)
|
||||||
|
|
||||||
|
This function frees the literal at the position represented by the integer literal `index`, and stores `value` in its place.
|
||||||
|
|
||||||
|
This function returns true on success, otherwise it returns false.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index)
|
||||||
|
|
||||||
|
This function returns the literal at the position represented by the integer literal `index`, or returns a null literal if none is found.
|
||||||
|
|
||||||
|
If `index` is not an integer literal or is out of bounds, this function returns a null literal.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### int Toy_private_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal)
|
||||||
|
|
||||||
|
This function scans through the array, and returns the index of the first element that matches the given `literal`, otherwise it returns -1.
|
||||||
|
|
||||||
|
Private functions are not intended for general use.
|
||||||
|
!*/
|
||||||
|
int Toy_private_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
|
||||||
|
|
||||||
|
//TODO: add a function to get the capacity & count
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
#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) {
|
||||||
|
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,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_literal_dictionary.h
|
||||||
|
|
||||||
|
This header defines the dictionary structure (as well as the private entry structure), which manages a series of `Toy_Literal` instances stored in a key-value hash map. The dictionary does not take ownership of given literals, instead it makes an internal copy.
|
||||||
|
|
||||||
|
The dictionary type is one of two fundemental data structures used throughout Toy - the other is the array.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_literal.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Macros
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_DICTIONARY_MAX_LOAD
|
||||||
|
|
||||||
|
If the contents of a dictionary exceeds this percentage of it's capacity, then a new buffer is created, the old contents are copied over one-by-one, and the original buffer is freed.
|
||||||
|
|
||||||
|
Since this process can be memory and time intensive, a configurable macro is used to allow for fine-grained control across the lang.
|
||||||
|
|
||||||
|
The current default value is `0.75`, representing 75% capacity.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
//TODO: benchmark this
|
||||||
|
#define TOY_DICTIONARY_MAX_LOAD 0.75
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary)
|
||||||
|
|
||||||
|
This function initializes the `Toy_LiteralDictionary` pointed to by `dictionary`.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary)
|
||||||
|
|
||||||
|
This function frees a `Toy_LiteralDictionary` pointed to by `dictionary`. Every literal within is passed to `Toy_freeLiteral()` before its memory is released.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value)
|
||||||
|
|
||||||
|
This function inserts the given key-value pair of literals into `dictionary`, creating it if it doesn't exist, or freeing and overwriting it if `key` is already present. This function may also expand the memory buffer if needed.
|
||||||
|
|
||||||
|
When expanding the memory buffer, a full copy of the existing dictionary's contents is created - this can be memory intensive.
|
||||||
|
|
||||||
|
Literal functions and opaques cannot be used as keys.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
|
||||||
|
|
||||||
|
This function returns the value of the literal within `dictionary` identified by `key`, or a null literal if it doesn't exist.
|
||||||
|
|
||||||
|
Literal functions and opaques cannot be used as keys.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
|
||||||
|
|
||||||
|
This function removes the key-value pair of literals from `dictionary` identified by `key`, if it exists.
|
||||||
|
|
||||||
|
Literal functions and opaques cannot be used as keys.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
|
||||||
|
|
||||||
|
This function returns true if the key-value pair identified by `key` exists within `dictionary`, otherwise it returns false.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||||
@@ -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 %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)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,112 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_memory.h
|
||||||
|
|
||||||
|
This header defines all of the memory management utilities. Any and all heap-based memory management goes through these utilities.
|
||||||
|
|
||||||
|
A default memory allocator function is used internally, but it can be overwritten for diagnostic and platform related purposes.
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Macros
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_GROW_CAPACITY(capacity)
|
||||||
|
|
||||||
|
This macro calculates, in place, what size of memory should be allocated based on the previous size.
|
||||||
|
!*/
|
||||||
|
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_GROW_CAPACITY_FAST(capacity)
|
||||||
|
|
||||||
|
This macro calculates, in place, what size of memory should be allocated based on the previous size. It grows faster than `TOY_GROW_CAPACITY`.
|
||||||
|
!*/
|
||||||
|
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
### TOY_ALLOCATE(type, count)
|
||||||
|
|
||||||
|
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be allocated, and `count` is the number which will be needed (usually calculated with `TOY_GROW_CAPACITY`).
|
||||||
|
|
||||||
|
This returns a pointer of `type`.
|
||||||
|
*/
|
||||||
|
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_FREE(type, pointer)
|
||||||
|
|
||||||
|
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be freed, and `pointer` is to what is being freed. This should only be used when a single element has been allocated, as opposed to an array.
|
||||||
|
!*/
|
||||||
|
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_FREE_ARRAY(type, pointer, oldCount)
|
||||||
|
|
||||||
|
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be freed, `pointer` is a reference to what is being freed, and `oldCount` is the size of the array being freed. This should only be used when an array has been allocated, as opposed to a single element.
|
||||||
|
!*/
|
||||||
|
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_GROW_ARRAY(type, pointer, oldCount, count)
|
||||||
|
|
||||||
|
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that is being operated on, `pointer` is what is being resized, `oldCount` is the previous size of the array and `count` is the new size of the array (usually calculated with `TOY_GROW_CAPACITY`).
|
||||||
|
|
||||||
|
This returns a pointer of `type`.
|
||||||
|
!*/
|
||||||
|
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_SHRINK_ARRAY(type, pointer, oldCount, count)
|
||||||
|
|
||||||
|
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that is being operated on, `pointer` is what is being resized, `oldCount` is the previous size of the array and `count` is the new size of the array.
|
||||||
|
|
||||||
|
This returns a pointer of `type`.
|
||||||
|
!*/
|
||||||
|
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Interfaces
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
|
||||||
|
|
||||||
|
This function interface is used for defining any memory allocator functions.
|
||||||
|
|
||||||
|
Any and all memory allocator functions should:
|
||||||
|
|
||||||
|
* Take a `pointer` to a previously allocated block of memory, or `NULL`
|
||||||
|
* Take the `oldSize`, which is the previous size of the `pointer` allocated, in bytes (`oldSize` can be 0)
|
||||||
|
* Take the `newSize`, which is the new size of the buffer to be allocaated, in bytes (`newSize` can be 0)
|
||||||
|
* Return the newly allocated buffer, or `NULL` if `newSize` is zero
|
||||||
|
* Return `NULL` on error
|
||||||
|
!*/
|
||||||
|
|
||||||
|
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize)
|
||||||
|
|
||||||
|
This function shouldn't be called directly. Instead, use one of the given macros.
|
||||||
|
|
||||||
|
This function wraps a call to the internal assigned memory allocator.
|
||||||
|
!*/
|
||||||
|
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn)
|
||||||
|
|
||||||
|
This function sets the memory allocator, replacing the default memory allocator.
|
||||||
|
|
||||||
|
This function also overwrites any given refstring and reffunction memory allocators, see [toy_refstring.h](toy_refstring_h.md).
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum Toy_Opcode {
|
||||||
|
TOY_OP_EOF,
|
||||||
|
|
||||||
|
//do nothing
|
||||||
|
TOY_OP_PASS,
|
||||||
|
|
||||||
|
//basic statements
|
||||||
|
TOY_OP_ASSERT,
|
||||||
|
TOY_OP_PRINT,
|
||||||
|
|
||||||
|
//data
|
||||||
|
TOY_OP_LITERAL,
|
||||||
|
TOY_OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
||||||
|
TOY_OP_LITERAL_RAW, //forcibly get the raw value of the literal
|
||||||
|
|
||||||
|
//arithmetic operators
|
||||||
|
TOY_OP_NEGATE,
|
||||||
|
TOY_OP_ADDITION,
|
||||||
|
TOY_OP_SUBTRACTION,
|
||||||
|
TOY_OP_MULTIPLICATION,
|
||||||
|
TOY_OP_DIVISION,
|
||||||
|
TOY_OP_MODULO,
|
||||||
|
TOY_OP_GROUPING_BEGIN,
|
||||||
|
TOY_OP_GROUPING_END,
|
||||||
|
|
||||||
|
//variable stuff
|
||||||
|
TOY_OP_SCOPE_BEGIN,
|
||||||
|
TOY_OP_SCOPE_END,
|
||||||
|
|
||||||
|
TOY_OP_TYPE_DECL_removed,
|
||||||
|
TOY_OP_TYPE_DECL_LONG_removed,
|
||||||
|
|
||||||
|
TOY_OP_VAR_DECL, //declare a variable to be used (as a literal)
|
||||||
|
TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
||||||
|
|
||||||
|
TOY_OP_FN_DECL, //declare a function to be used (as a literal)
|
||||||
|
TOY_OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
|
||||||
|
|
||||||
|
TOY_OP_VAR_ASSIGN, //assign to a literal
|
||||||
|
TOY_OP_VAR_ADDITION_ASSIGN,
|
||||||
|
TOY_OP_VAR_SUBTRACTION_ASSIGN,
|
||||||
|
TOY_OP_VAR_MULTIPLICATION_ASSIGN,
|
||||||
|
TOY_OP_VAR_DIVISION_ASSIGN,
|
||||||
|
TOY_OP_VAR_MODULO_ASSIGN,
|
||||||
|
|
||||||
|
TOY_OP_TYPE_CAST, //temporarily change a type of an atomic value
|
||||||
|
TOY_OP_TYPE_OF, //get the type of a variable
|
||||||
|
|
||||||
|
TOY_OP_IMPORT,
|
||||||
|
TOY_OP_EXPORT_removed,
|
||||||
|
|
||||||
|
//for indexing
|
||||||
|
TOY_OP_INDEX,
|
||||||
|
TOY_OP_INDEX_ASSIGN,
|
||||||
|
TOY_OP_INDEX_ASSIGN_INTERMEDIATE,
|
||||||
|
TOY_OP_DOT,
|
||||||
|
|
||||||
|
//comparison of values
|
||||||
|
TOY_OP_COMPARE_EQUAL,
|
||||||
|
TOY_OP_COMPARE_NOT_EQUAL,
|
||||||
|
TOY_OP_COMPARE_LESS,
|
||||||
|
TOY_OP_COMPARE_LESS_EQUAL,
|
||||||
|
TOY_OP_COMPARE_GREATER,
|
||||||
|
TOY_OP_COMPARE_GREATER_EQUAL,
|
||||||
|
TOY_OP_INVERT, //for booleans
|
||||||
|
|
||||||
|
//logical operators
|
||||||
|
TOY_OP_AND,
|
||||||
|
TOY_OP_OR,
|
||||||
|
|
||||||
|
//jumps, and conditional jumps (absolute)
|
||||||
|
TOY_OP_JUMP,
|
||||||
|
TOY_OP_IF_FALSE_JUMP,
|
||||||
|
TOY_OP_FN_CALL,
|
||||||
|
TOY_OP_FN_RETURN,
|
||||||
|
|
||||||
|
//pop the stack at the end of a complex statement
|
||||||
|
TOY_OP_POP_STACK,
|
||||||
|
|
||||||
|
//ternary shorthand
|
||||||
|
TOY_OP_TERNARY,
|
||||||
|
|
||||||
|
//meta
|
||||||
|
TOY_OP_FN_END, //different from SECTION_END
|
||||||
|
TOY_OP_SECTION_END = 255,
|
||||||
|
//TODO: add more
|
||||||
|
|
||||||
|
//prefix & postfix signals (used internally)
|
||||||
|
TOY_OP_PREFIX,
|
||||||
|
TOY_OP_POSTFIX,
|
||||||
|
} Toy_Opcode;
|
||||||
|
|
||||||
+1958
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_parser.h
|
||||||
|
|
||||||
|
This header defines the parser structure which, after being initialized with a lexer produces a series of abstract syntax trees to be passed to the compiler. The following is a utility function provided by [repl_tools.h](repl_tools_h.md), demonstrating how to use the parser.
|
||||||
|
|
||||||
|
```c
|
||||||
|
//generate bytecode from a given string
|
||||||
|
const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
||||||
|
//declare the relevant instances
|
||||||
|
Toy_Lexer lexer;
|
||||||
|
Toy_Parser parser;
|
||||||
|
Toy_Compiler compiler;
|
||||||
|
|
||||||
|
//initialize each of them
|
||||||
|
Toy_initLexer(&lexer, source);
|
||||||
|
Toy_initParser(&parser, &lexer);
|
||||||
|
Toy_initCompiler(&compiler);
|
||||||
|
|
||||||
|
//when the parser returns NULL, it is finished
|
||||||
|
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||||
|
while(node != NULL) {
|
||||||
|
//if the parser returns an error node, clean up and exit gracefully
|
||||||
|
if (node->type == TOY_AST_NODE_ERROR) {
|
||||||
|
Toy_freeASTNode(node);
|
||||||
|
Toy_freeCompiler(&compiler);
|
||||||
|
Toy_freeParser(&parser);
|
||||||
|
//no need to clean the lexer
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//write the node to the compiler
|
||||||
|
Toy_writeCompiler(&compiler, node);
|
||||||
|
Toy_freeASTNode(node);
|
||||||
|
|
||||||
|
//grab the next node
|
||||||
|
node = Toy_scanParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the bytecode to be returned
|
||||||
|
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeCompiler(&compiler);
|
||||||
|
Toy_freeParser(&parser);
|
||||||
|
//no need to clean the lexer
|
||||||
|
|
||||||
|
//finally
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_lexer.h"
|
||||||
|
#include "toy_ast_node.h"
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer)
|
||||||
|
|
||||||
|
This function initializes a `Toy_Parser`, binding the given `Toy_Lexer` to it.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeParser(Toy_Parser* parser)
|
||||||
|
|
||||||
|
This function frees a `Toy_Parser` once its task is completed.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_freeParser(Toy_Parser* parser);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_ASTNode* Toy_scanParser(Toy_Parser* parser)
|
||||||
|
|
||||||
|
This function returns an abstract syntax tree representing part of the program, or an error node. The abstract syntax tree must be passed to `Toy_writeCompiler()` and/or `Toy_freeASTNode()`.
|
||||||
|
|
||||||
|
This function should be called repeatedly until it returns `NULL`, indicating the end of the program.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_freeASTNode(Toy_ASTNode* node)
|
||||||
|
|
||||||
|
This function cleans up any valid instance of `Toy_ASTNode` pointer passed to it. It is most commonly used to clean up the values returned by `Toy_scanParser`, after they have been passsed to `Toy_writeCompiler`, or when the node is an error node.
|
||||||
|
|
||||||
|
Note: this function is *actually* defined in toy_ast_node.h, but documented here, because this is where it matters most.
|
||||||
|
!*/
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include "toy_reffunction.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//memory allocation
|
||||||
|
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
static Toy_RefFunctionAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
|
||||||
|
|
||||||
|
void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn allocator) {
|
||||||
|
allocate = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
//API
|
||||||
|
Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length) {
|
||||||
|
//allocate the memory area (including metadata space)
|
||||||
|
Toy_RefFunction* refFunction = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * length);
|
||||||
|
|
||||||
|
if (refFunction == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the data
|
||||||
|
refFunction->refCount = 1;
|
||||||
|
refFunction->length = length;
|
||||||
|
memcpy(refFunction->data, data, refFunction->length);
|
||||||
|
|
||||||
|
return refFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_deleteRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
//decrement, then check
|
||||||
|
refFunction->refCount--;
|
||||||
|
if (refFunction->refCount <= 0) {
|
||||||
|
allocate(refFunction, sizeof(size_t) + sizeof(int) + sizeof(char) * (refFunction->length + 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Toy_countRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
return refFunction->refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
return refFunction->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
//Cheaty McCheater Face
|
||||||
|
refFunction->refCount++;
|
||||||
|
return refFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction) {
|
||||||
|
//create a new function, with a new refCount
|
||||||
|
return Toy_createRefFunction(refFunction->data, refFunction->length);
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_reffunction.h
|
||||||
|
|
||||||
|
This header defines the Toy_RefFunction structure, as well as all of the related utilities.
|
||||||
|
|
||||||
|
See [Toy_RefString](toy_refstring_h.md) for more information about the reference pattern.
|
||||||
|
|
||||||
|
This module reserves the right to instead preform a deep copy when it sees fit (this is for future debugging purposes).
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
//the RefFunction structure
|
||||||
|
typedef struct Toy_RefFunction {
|
||||||
|
size_t length;
|
||||||
|
int refCount;
|
||||||
|
unsigned char data[];
|
||||||
|
} Toy_RefFunction;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Interfaces
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
|
||||||
|
|
||||||
|
This interface conforms to Toy's memory API, and generally shouldn't be used without a good reason.
|
||||||
|
!*/
|
||||||
|
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn)
|
||||||
|
|
||||||
|
This function conforms to and is invoked by Toy's memory API, and generally shouldn't be used without a good reason.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length)
|
||||||
|
|
||||||
|
This function returns a new `Toy_RefFunction`, containing a copy of `data`, or `NULL` on error.
|
||||||
|
|
||||||
|
This function also sets the returned `refFunction`'s reference counter to 1.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_deleteRefFunction(Toy_RefFunction* refFunction)
|
||||||
|
|
||||||
|
This function reduces the `refFunction`'s reference counter by 1 and, if it reaches 0, frees the memory.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### int Toy_countRefFunction(Toy_RefFunction* refFunction)
|
||||||
|
|
||||||
|
This function returns the total number of references to `refFunction`, for debugging.
|
||||||
|
!*/
|
||||||
|
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction)
|
||||||
|
|
||||||
|
This function returns the length of the underlying bytecode of `refFunction`.
|
||||||
|
!*/
|
||||||
|
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction)
|
||||||
|
|
||||||
|
This function increases the reference counter of `refFunction` by 1, before returning the given pointer.
|
||||||
|
|
||||||
|
This function reserves the right to create a deep copy where needed.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction)
|
||||||
|
|
||||||
|
This function behaves identically to `Toy_copyRefFunction`, except that it explicitly forces a deep copy of the internal memory. Using this function should be done carefully, as it incurs a performance penalty that negates the benefit of this module.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
|
||||||
@@ -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,125 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*!
|
||||||
|
# toy_refstring.h
|
||||||
|
|
||||||
|
This header defines the structure `Toy_RefString`, as well as all of the related utilities.
|
||||||
|
|
||||||
|
[refstring](https://github.com/Ratstail91/refstring) is a stand-alone utility written to reduce the amount of memory manipulation used within Toy. It was independantly written and tested, before being incorporated into Toy proper. As such it has it's own memory management API, which by default is tied into Toy's [core memory API](toy_memory_h.md).
|
||||||
|
|
||||||
|
Instances of `Toy_RefString` are reference counted - that is, rather than copying an existing string in memory, a pointer to the refstring is returned, and the internal reference counter is increased by 1. When the pointer is no longer needed, `Toy_DeleteRefString` can be called; this will decrement the internal reference counter by 1, and only free it when it reaches 0. This has multiple benefits, when used correctly:
|
||||||
|
|
||||||
|
* Reduced memory usage
|
||||||
|
* Faster program execution
|
||||||
|
|
||||||
|
This module reserves the right to instead preform a deep copy when it sees fit (this is for future debugging purposes).
|
||||||
|
!*/
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//the RefString structure
|
||||||
|
typedef struct Toy_RefString {
|
||||||
|
size_t length;
|
||||||
|
int refCount;
|
||||||
|
char data[];
|
||||||
|
} Toy_RefString;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Interfaces
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
|
||||||
|
|
||||||
|
This interface conforms to Toy's memory API, and generally shouldn't be used without a good reason.
|
||||||
|
!*/
|
||||||
|
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
## Defined Functions
|
||||||
|
!*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn)
|
||||||
|
|
||||||
|
This function conforms to and is invoked by Toy's memory API, and generally shouldn't be used without a good reason.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefString* Toy_createRefString(const char* cstring)
|
||||||
|
|
||||||
|
This function wraps `Toy_CreateRefStringLength`, by determining the length of the given `cstring` and passing it to the other function.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefString* Toy_createRefString(const char* cstring);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length)
|
||||||
|
|
||||||
|
This function returns a new `Toy_RefString`, containing a copy of `cstring`, or `NULL` on error.
|
||||||
|
|
||||||
|
This function also sets the returned refstring's reference counter to 1.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### void Toy_deleteRefString(Toy_RefString* refString)
|
||||||
|
|
||||||
|
This function reduces the `refString`'s reference counter by 1 and, if it reaches 0, frees the memory.
|
||||||
|
!*/
|
||||||
|
TOY_API void Toy_deleteRefString(Toy_RefString* refString);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### int Toy_countRefString(Toy_RefString* refString)
|
||||||
|
|
||||||
|
This function returns the total number of references to `refString`, for debugging.
|
||||||
|
!*/
|
||||||
|
TOY_API int Toy_countRefString(Toy_RefString* refString);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### size_t Toy_lengthRefString(Toy_RefString* refString)
|
||||||
|
|
||||||
|
This function returns the length of the underlying cstring of `refString`.
|
||||||
|
!*/
|
||||||
|
TOY_API size_t Toy_lengthRefString(Toy_RefString* refString);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefString* Toy_copyRefString(Toy_RefString* refString)
|
||||||
|
|
||||||
|
This function increases the reference counter of `refString` by 1, before returning the given pointer.
|
||||||
|
|
||||||
|
This function reserves the right to create a deep copy where needed.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString)
|
||||||
|
|
||||||
|
This function behaves identically to `Toy_copyRefString`, except that it explicitly forces a deep copy of the internal memory. Using this function should be done carefully, as it incurs a performance penalty that negates the benefit of this module.
|
||||||
|
!*/
|
||||||
|
TOY_API Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### const char* Toy_toCString(Toy_RefString* refString)
|
||||||
|
|
||||||
|
This function exposes the interal cstring of `refString`. Only use this function when dealing with external APIs.
|
||||||
|
!*/
|
||||||
|
TOY_API const char* Toy_toCString(Toy_RefString* refString);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs)
|
||||||
|
|
||||||
|
This function returns true when the two refstrings are either the same refstring, or contain the same value. Otherwise it returns false.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
### bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring)
|
||||||
|
|
||||||
|
This function returns true when the `refString` contains the same value as the `cstring`. Otherwise it returns false.
|
||||||
|
!*/
|
||||||
|
TOY_API bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
|
||||||
|
|
||||||
|
//TODO: merge refstring memory
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user