Compare commits
283 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 813da3e1aa | |||
| 750ebd1f99 | |||
| f55f27726c | |||
| a0d75b3c70 | |||
| 414a9d6194 | |||
| 8013ccb1da | |||
| 645fc3a457 | |||
| 2eaf0a9b0f | |||
| 3ab18c7b14 | |||
| 1660dc8b53 | |||
| b3f427d80d | |||
| 6901b9a6c9 | |||
| c9d4b9965c | |||
| ff1ef1352a | |||
| 53d3606c7e | |||
| 83fb5222a2 | |||
| 3b813da1cf | |||
| 8b9b012bcc | |||
| 6c055a0435 | |||
| be84a8dfe2 | |||
| 185f3896c5 | |||
| 60a0fe8907 | |||
| 7cdb81e0bf | |||
| 18a4b33c4e | |||
| eb33775314 | |||
| 617a658de0 | |||
| 6ebbcc45a3 | |||
| b718b35097 | |||
| af30246e0c | |||
| efc9fe1406 | |||
| c9a34e2259 | |||
| 0c24a7609e | |||
| 88e9794952 | |||
| b589392b9e | |||
| 21819b2d62 | |||
| 4aec343b6c | |||
| 9a75226491 | |||
| d2ac1eeb8e | |||
| f8596806ee | |||
| 4957536e23 | |||
| 5a867ac627 | |||
| 991d29e3e6 | |||
| accb7f9fb4 | |||
| 63dfd33e5e | |||
| 9bb115f732 | |||
| 8875b6968b | |||
| 97d16c1184 | |||
| 47c5d49069 | |||
| 2c92f829e1 | |||
| 5b101d763e | |||
| 81c95ff69d | |||
| 88100b128a | |||
| 3a0f11ebb4 | |||
| f9790b99ce | |||
| dde52f9d8a | |||
| cda4bee6ee | |||
| 9a10fadada | |||
| 8eefbc8a0c | |||
| e24823924a | |||
| c0c03a4110 | |||
| b0d9c15d33 | |||
| 24e5d8081c | |||
| 42aef306a9 | |||
| 6f27d07829 | |||
| b32ea9f309 | |||
| 49a825aaf9 | |||
| baa81b1aa9 | |||
| 9553edef9c | |||
| 66155fa213 | |||
| 547229e150 | |||
| 211744535e | |||
| 3a24fbf6e1 | |||
| 842f041a50 | |||
| 7408a24a12 | |||
| 09fc6d5279 | |||
| f25e81cd09 | |||
| 48072f0dd1 | |||
| 522fc3e64b | |||
| f4ce6ad9f1 | |||
| 3f35502694 | |||
| f06218b9cd | |||
| 1ae3fcbf73 | |||
| abae97b6e5 | |||
| fbb7e1bc54 | |||
| 57fe9bb00d | |||
| 914ee6fcfa | |||
| ba9418f365 | |||
| 98208f4bb5 | |||
| 5a0012d73a | |||
| 24d111e52d | |||
| 4b21e61df0 | |||
| d3b59eb0da | |||
| 9fe6d6b218 | |||
| 3a82593e4d | |||
| 639250f028 | |||
| 02dfc996b4 | |||
| 76d89fe0ad | |||
| 15aea7c032 | |||
| 574502bf3e | |||
| 258968d7a4 | |||
| 7a8c415b3f | |||
| 344c265918 | |||
| f2660b95eb | |||
| fe64e876bd | |||
| 1006b6e216 | |||
| a1cfc095a7 | |||
| 7c054db9e6 | |||
| 72f4e4c143 | |||
| e2a284d9ce | |||
| c646904407 | |||
| 470836a390 | |||
| 212eca1825 | |||
| b93ea5006c | |||
| 336616a1bf | |||
| 63cc530899 | |||
| 3c0a50c4cd | |||
| 481d17f040 | |||
| bfed4e23f3 | |||
| 002651f95d | |||
| a1f6f147c5 | |||
| f9715c2016 | |||
| 6c293cfc62 | |||
| 05451af8d7 | |||
| 730353342d | |||
| 513d8f130c | |||
| 13daf95933 | |||
| 9141102f2e | |||
| ce03a342c9 | |||
| c6d8766bc3 | |||
| 14696833fd | |||
| 6f16c31f24 | |||
| 3aee2ba664 | |||
| b55192e513 | |||
| 90ffe9b40e | |||
| 9de9c85bea | |||
| 23eb3e45df | |||
| b84a70cc34 | |||
| 6d25beea03 | |||
| e96f87ff45 | |||
| 0192b60338 | |||
| c3a737eae5 | |||
| cc4ff3f6d8 | |||
| 153ab54be6 | |||
| 24cfe7f539 | |||
| 3ca816439e | |||
| 9cb138a7d6 | |||
| 9e2cbb1f59 | |||
| b092b8ce50 | |||
| 4faa0c0476 | |||
| 223db840c8 | |||
| 8b5cc3b493 | |||
| 04c799954c | |||
| 3e17916a4a | |||
| a28053d4e9 | |||
| 93fce94e9c | |||
| 7be63c8ccc | |||
| cf9affe190 | |||
| fce71a6cda | |||
| 5f4dfdccc5 | |||
| 476a79b746 | |||
| 5f75b5f1a3 | |||
| 1a36c14247 | |||
| 61a105db2d | |||
| 2324150fd5 | |||
| 03dce296cb | |||
| 62ca7a1fb7 | |||
| 12c6ac938c | |||
| 58cecafb3b | |||
| bb2e85e350 | |||
| 431893bf60 | |||
| 6cc331d462 | |||
| 0947430c29 | |||
| 1695f9d1c9 | |||
| 79c4374a1f | |||
| 37fb3927a6 | |||
| 0b559ecb74 | |||
| 7d4ea4881f | |||
| b29a87840d | |||
| b2b2ca7e53 | |||
| 34577ecfe1 | |||
| 7398898a61 | |||
| 2f9489d5fd | |||
| 04f0653595 | |||
| cd2113594d | |||
| be7e4ddd18 | |||
| b74aa63c1c | |||
| 935993ee8a | |||
| 436bd3ffca | |||
| 1608a13b43 | |||
| 1925d41940 | |||
| 5588986042 | |||
| 32727f986c | |||
| 92955d56dd | |||
| ef72415c21 | |||
| 7173f7770f | |||
| 3ad53c5e0e | |||
| 3cb2132bfa | |||
| feab02e790 | |||
| 163c8aa7f6 | |||
| d19ca1bcee | |||
| b30a092ab8 | |||
| e3bb7c0d25 | |||
| c5206daaea | |||
| d22b18ed17 | |||
| 2ee19c7c66 | |||
| 3148a56ce0 | |||
| 5b17c5e1e9 | |||
| ddd91e389e | |||
| 5d37d06343 | |||
| 787a1cca84 | |||
| 98ea0e5884 | |||
| c3ee92fcef | |||
| 09e4cb7b03 | |||
| 425ef7e3e0 | |||
| 694e262ee2 | |||
| a51c5591ee | |||
| 80734563b9 | |||
| 1ad6bdff70 | |||
| bfc8fe3717 | |||
| 7b1dbf25ff | |||
| c1d72adb71 | |||
| 87793b694a | |||
| 8d1e4d647b | |||
| e6fa345fe6 | |||
| ca4073a5ae | |||
| 9f45925072 | |||
| 0779798347 | |||
| 93f771dc8d | |||
| 4bcf8e84a9 | |||
| 14653a303f | |||
| d62ee2a9a3 | |||
| ff13b5cf38 | |||
| 956ebbeb28 | |||
| 4805c6757a | |||
| ad44eeac48 | |||
| d19a90f9bd | |||
| 29f5647e31 | |||
| 196b5f86f1 | |||
| 5cf2e70b7d | |||
| a0d616f412 | |||
| ab1c9b941f | |||
| 5db3e407b1 | |||
| 71c065a6c4 | |||
| 7b453bc35f | |||
| 53b0fc158c | |||
| a9759384cd | |||
| 7b8ff8f873 | |||
| 8d6bdb88b4 | |||
| c1f2e19e55 | |||
| 72cad02501 | |||
| 57fb1ece81 | |||
| 3d1d3b3b77 | |||
| c518960171 | |||
| 0504f4af8b | |||
| 7d92101c1f | |||
| c180984120 | |||
| a2625fa6d4 | |||
| 4567484038 | |||
| ac89f80b5b | |||
| 78320e53bb | |||
| 84f26d83c8 | |||
| ad6f1c3067 | |||
| e35af3d84b | |||
| 083ee950dd | |||
| 47ac1c5b30 | |||
| 898b8efc04 | |||
| b00a6838be | |||
| eca3350c64 | |||
| 5fd933a15e | |||
| 81417e7f32 | |||
| 023cf9c8b5 | |||
| 65087b18bd | |||
| e65555ca8a | |||
| 0ed676d79b | |||
| e6ad46f1ff | |||
| 190294e5d9 | |||
| 2370d5dc83 | |||
| a912b6a29c | |||
| 82f63013d8 | |||
| 0b8bf4119f | |||
| 607dc2c22a | |||
| fbcb2e0331 | |||
| 361fa78ffb |
@@ -1,43 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
name: Continuous Integration v2.x
|
||||||
|
|
||||||
|
#trigger when these occur
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- v2
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
branches:
|
||||||
|
- v2
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
#CI workflows across all supported platforms
|
||||||
|
standard:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platforms:
|
||||||
|
- ubuntu-latest
|
||||||
|
- windows-latest
|
||||||
|
- macos-latest
|
||||||
|
runs-on: ${{ matrix.platforms }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Run all tests
|
||||||
|
run: make tests
|
||||||
|
|
||||||
|
gdb:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install GDB if not present
|
||||||
|
run: sudo apt update && sudo apt install gdb
|
||||||
|
- name: Run all tests under gdb
|
||||||
|
run: make tests-gdb
|
||||||
+4
-6
@@ -41,6 +41,7 @@
|
|||||||
*.su
|
*.su
|
||||||
*.idb
|
*.idb
|
||||||
*.pdb
|
*.pdb
|
||||||
|
*.log
|
||||||
|
|
||||||
# Kernel Module Compile Results
|
# Kernel Module Compile Results
|
||||||
*.mod*
|
*.mod*
|
||||||
@@ -51,9 +52,6 @@ Module.symvers
|
|||||||
Mkfile.old
|
Mkfile.old
|
||||||
dkms.conf
|
dkms.conf
|
||||||
|
|
||||||
.cproject
|
#mdbook files
|
||||||
.project
|
book
|
||||||
.settings/
|
mdbook
|
||||||
temp/
|
|
||||||
Release/
|
|
||||||
out/
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
This folder is full of development notes, and are probably out of date. Check the actual docs for the correct info.
|
||||||
|
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
This file is messy and confusing, and makes sense to nobody but me - so don't worry about understanding it too much - better docs will come later.
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
SECD = State, Environment, Control, Dump
|
||||||
|
|
||||||
|
The idea of "Landin's SECD Machine" is to store the working memory in S, the variable-value bindings in E, the code/instructions in C, and the program stack in D.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
DEFINE = DECLARE + SET
|
||||||
|
|
||||||
|
The environment, denoted with an E, is created on routine start, and destroyed on routine end - however, it uses the parent routine's environment as the starting point for it's creation, so closures work as expected
|
||||||
|
|
||||||
|
unlike version 1, identifiers are not a valid datatype - they're just an index representing a symbol, like "standard::clock"
|
||||||
|
|
||||||
|
meta opcodes - EOF, PASS, ERROR,
|
||||||
|
|
||||||
|
a "value" can be of any valid datatype, and may point to various parts of memory to define it's value
|
||||||
|
|
||||||
|
Symbols will be awkward... I suspect the symbol table might need to be rebuilt on startup, as the order of the modules will not necessarily be the same each time
|
||||||
|
|
||||||
|
The various instances of S could be the same array in memory, simply marked as "unused"? You could stick C on there as a value before "pushing" for a new routine
|
||||||
|
|
||||||
|
Things to consider later:
|
||||||
|
type cast?
|
||||||
|
rest parameter?
|
||||||
|
index access and assign?
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
//variable instructions
|
||||||
|
READ
|
||||||
|
read one value from C onto S
|
||||||
|
LOAD
|
||||||
|
read one value from .data onto S
|
||||||
|
DECLARE
|
||||||
|
read two words from C, create a new entry in E with the key E[SYMBOL(word1)], the type defined by word2, the value 'null'
|
||||||
|
DEFINE
|
||||||
|
read one word from C, saves the pre-existing key E[SYMBOL(word)] to the value S(0), popping S(0)
|
||||||
|
ACCESS
|
||||||
|
read one word from C, finds the pre-existing value of E[SYMBOL(word)], leaves the value on S
|
||||||
|
|
||||||
|
//arithmetic instructions
|
||||||
|
ADD
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
SUBTRACT
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
MULTIPLY
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
DIVIDE
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
MODULO
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
|
||||||
|
|
||||||
|
//comparison instructions
|
||||||
|
COMPARE_EQUAL
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on equality
|
||||||
|
COMPARE_LESS
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
COMPARE_LESS_EQUAL
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
COMPARE_GREATER
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
COMPARE_GREATER_EQUAL
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
|
||||||
|
|
||||||
|
//logical instructions
|
||||||
|
AND
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
OR
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
TRUTHY
|
||||||
|
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
NEGATE
|
||||||
|
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
|
||||||
|
|
||||||
|
//control instructions
|
||||||
|
JUMP
|
||||||
|
read one value from C, and move the program counter to that location (relative to the current position)
|
||||||
|
JUMP_IF_FALSE
|
||||||
|
read one value from C, pops S(0), and move the program counter to that location (relative to the current position) if the popped value is falsy
|
||||||
|
FN_CALL
|
||||||
|
*read a list of arguments specified in C into 'A', store (S, E, C, D) as D, push S, move the stack pointer to the specified routine, push a new E based on the contents of 'A'
|
||||||
|
FN_RETURN
|
||||||
|
*read a list of return values specified in C into 'R', pop S, restore (S, E, C, D) from D(0) popping it, store the contents of 'R' in E or S based on the next few parts of C
|
||||||
|
|
||||||
|
//various action instructions
|
||||||
|
ASSERT
|
||||||
|
if S(-1) is falsy, print S(0) and exit
|
||||||
|
PRINT
|
||||||
|
pop S(0), and print the output
|
||||||
|
IMPORT
|
||||||
|
//invoke an external library into the current scope
|
||||||
|
CONCAT
|
||||||
|
//combine two strings
|
||||||
|
SCOPE_BEGIN
|
||||||
|
//push an inner environment to E, which should be automatically popped at the routine's end
|
||||||
|
SCOPE_END
|
||||||
|
//pop an inner environment from E, only if it was created with SCOPE_BEGIN
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
FN_CALL
|
||||||
|
read word: read the following N arguments
|
||||||
|
|
||||||
|
for 0 to N do:
|
||||||
|
read word as match: # this allows literals and identifiers as arguments
|
||||||
|
stack: then pop S(0) into 'A'
|
||||||
|
**env: then read word, load E[SYMBOL(word)] into 'A'
|
||||||
|
|
||||||
|
read word:
|
||||||
|
determine where the routine is (is it new or is it a value?) and hold it for a moment
|
||||||
|
push E and C into a frame marker on S
|
||||||
|
jump C to the routine
|
||||||
|
|
||||||
|
read word:
|
||||||
|
read the following N parameter names, storing each member of 'A' as their value in E[SYMBOL(name)]
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
FN_RETURN
|
||||||
|
read word: read the following N return values
|
||||||
|
|
||||||
|
for 0 to N do:
|
||||||
|
read word as match: # this allows literals and identifiers as arguments
|
||||||
|
stack: then pop S(0) into 'R'
|
||||||
|
**env: then read word, load E[SYMBOL(word)] into 'R'
|
||||||
|
|
||||||
|
pop E and S
|
||||||
|
extract and restore E and C from the frame marker on S
|
||||||
|
|
||||||
|
read word: read the following N storage locations for the values within `R`
|
||||||
|
|
||||||
|
for 0 to N do:
|
||||||
|
read word as match: # you're effectively reversing the prior reads
|
||||||
|
stack: then push from 'R' onto S
|
||||||
|
**env: then read word, save 'R' into E[SYMBOL(word)]
|
||||||
|
|
||||||
|
**This could work by listing the sources as e.g. "SSSExS" - three stacks and one environment variable loaded onto the stack, then one more stack for a total of four values
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
the bytecode of a funtion call would look like:
|
||||||
|
|
||||||
|
FN_CALL N [stack|env word]... N [stack|env word]...
|
||||||
|
|
||||||
|
the value of C stored in D points to the second N, while it waits to pick up where it left off
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
The bytecode format
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
NOTE: This datestamp header is currently not implemented
|
||||||
|
|
||||||
|
There are four components in the datestamp header:
|
||||||
|
|
||||||
|
TOY_VERSION_MAJOR
|
||||||
|
TOY_VERSION_MINOR
|
||||||
|
TOY_VERSION_PATCH
|
||||||
|
TOY_VERSION_BUILD
|
||||||
|
|
||||||
|
The first three are each one unsigned byte, and the fourth is a null terminated C-string.
|
||||||
|
|
||||||
|
* Under no circumstance, should you ever run bytecode whose major version is different
|
||||||
|
* Under no circumstance, should you ever run bytecode whose minor version is above the interpreter’s minor version
|
||||||
|
* You may, at your own risk, attempt to run bytecode whose patch version is different from the interpreter’s patch version
|
||||||
|
* You may, at your own risk, attempt to run bytecode whose build version is different from the interpreter’s build version
|
||||||
|
|
||||||
|
An additional note: The contents of the build string may be anything, such as:
|
||||||
|
|
||||||
|
* the compilation date and time of the interpreter
|
||||||
|
* a marker identifying the current fork and/or branch
|
||||||
|
* identification information, such as the developer's copyright
|
||||||
|
* a link to Risk Astley's "Never Gonna Give You Up" on YouTube
|
||||||
|
|
||||||
|
Please note that in the final bytecode, if the null terminator of TOY_VERSION_BUILD is not 4-byte aligned, extra space will be allocated to round out the header's size to a multiple of 4. The contents of the extra bytes are undefined.
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
Bytecode Format Structure
|
||||||
|
|
||||||
|
.header:
|
||||||
|
N total size # size of this routine, including all data and subroutines
|
||||||
|
N .jumps count # the number of entries in the jump table (should be data count + routine count)
|
||||||
|
N .param count # the number of parameter fields expected (a secondary jump table, used for subroutine parameters)
|
||||||
|
N .data count # the number of data fields present
|
||||||
|
N .subs count # the number of subroutines present
|
||||||
|
.code start # absolute address of .code; mandatory
|
||||||
|
.param start # absolute addess of .param; omitted if not needed
|
||||||
|
.datatable start # absolute address of .datatable; omitted if not needed
|
||||||
|
.data start # absolute address of .data; omitted if not needed
|
||||||
|
.subs start # absolute address of .subs; omitted if not needed
|
||||||
|
# additional metadata fields can be added later
|
||||||
|
|
||||||
|
.code:
|
||||||
|
# opcode instructions read and 'executed' by the interpreter (aligned to 4-byte widths)
|
||||||
|
[READ, TOY_VALUE_STRING, Toy_StringType, stringLength] [jumpIndex]
|
||||||
|
|
||||||
|
.jumps:
|
||||||
|
# a layer of indirection for quickly looking up values in .data and .subs
|
||||||
|
0 -> {string, 0x00}
|
||||||
|
4 -> {fn, 0xFF}
|
||||||
|
|
||||||
|
.param:
|
||||||
|
# a list of names, stored in .data, to be used for any provided function arguments
|
||||||
|
|
||||||
|
.data:
|
||||||
|
# data that can't be cleanly embedded into .code, such as strings
|
||||||
|
"Hello world\0"
|
||||||
|
|
||||||
|
.subs:
|
||||||
|
# an extension of .data, used exclusively for subroutines (they also follow this spec, recursively)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint32_t hash (uint32_t x) {
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t unhash ( uint32_t x ) {
|
||||||
|
x = (( x >> 16) ^ x) * 0x119de1f3;
|
||||||
|
x = (( x >> 16) ^ x) * 0x119de1f3;
|
||||||
|
x = (( x >> 16) ^ x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
//I legit didn't know this algorithm could be reversed. Neat.
|
||||||
|
uint32_t value = 42;
|
||||||
|
printf("%u %u %u", value, hash(value), unhash(hash(value)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
The default version of GCC that ships on Raspian has an issue. The file '/usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so' has a faulty implementation of 'memcpy()' and possibly 'memset()'. Changing to the newer versions doens't work, as they're just symlinks to v7.
|
||||||
|
|
||||||
|
To resolve this, download and build this shared object:
|
||||||
|
|
||||||
|
https://github.com/simonjhall/copies-and-fills
|
||||||
|
|
||||||
|
Then, set the linker's preload value to point to that '.so' file (You may need to edit '/etc/ld.so.preload')
|
||||||
|
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"gruntfuggly.todo-tree"
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+66
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"todo-tree.filtering.includeGlobs": [
|
||||||
|
"**/repl/**",
|
||||||
|
"**/scripts/**",
|
||||||
|
"**/source/**",
|
||||||
|
"**/tests/**",
|
||||||
|
"**/tools/**",
|
||||||
|
],
|
||||||
|
"todo-tree.filtering.excludeGlobs": [
|
||||||
|
"**/obj/**",
|
||||||
|
"**/out/**",
|
||||||
|
],
|
||||||
|
"todo-tree.general.tags": [
|
||||||
|
"URGENT",
|
||||||
|
"BUG",
|
||||||
|
"TODO",
|
||||||
|
"WARN",
|
||||||
|
"BUGFIX",
|
||||||
|
"WONTFIX",
|
||||||
|
"NOTE"
|
||||||
|
],
|
||||||
|
"todo-tree.highlights.customHighlight": {
|
||||||
|
"URGENT": {
|
||||||
|
"icon": "alert",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#FF0000",
|
||||||
|
"foreground": "#FF0000"
|
||||||
|
},
|
||||||
|
"BUG": {
|
||||||
|
"icon": "bug",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#FF0000",
|
||||||
|
"foreground": "#FF0000"
|
||||||
|
},
|
||||||
|
"TODO": {
|
||||||
|
"icon": "alert",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#FFFF00",
|
||||||
|
"foreground": "#FFFF00"
|
||||||
|
},
|
||||||
|
"WARN": {
|
||||||
|
"icon": "alert",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#FFA500",
|
||||||
|
"foreground": "#FFA500"
|
||||||
|
},
|
||||||
|
"BUGFIX": {
|
||||||
|
"icon": "bug",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#00A000",
|
||||||
|
"foreground": "#00A000"
|
||||||
|
},
|
||||||
|
"WONTFIX": {
|
||||||
|
"icon": "bug",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#B64949",
|
||||||
|
"foreground": "#B64949"
|
||||||
|
},
|
||||||
|
"NOTE": {
|
||||||
|
"icon": "alert",
|
||||||
|
"type": "text",
|
||||||
|
"iconColour": "#00A000",
|
||||||
|
"foreground": "#00A000"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
No hating on other people, OK?
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
Copyright (c) 2020-2026 Kayne Ruse, KR Game Studios
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
# License
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
|
||||||
@@ -1,86 +1,75 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<image src="toylogo.png" />
|
<image src="toylogo.png" alt="The Toy Logo" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Toy v1
|
# Toy v2.x
|
||||||
|
|
||||||
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 Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through external scripts.
|
||||||
|
|
||||||
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 repository holds the reference implementation for Toy version 2.x, written in C - alpha testing is currently underway.
|
||||||
|
|
||||||
This is the Toy programming language interpreter, written in C.
|
|
||||||
|
|
||||||
# Nifty Features
|
# Nifty Features
|
||||||
|
|
||||||
* Simple C-like syntax
|
* Simple C-like syntax
|
||||||
* Bytecode intermediate compilation
|
* Intermediate AST and bytecode representations
|
||||||
* Optional, but robust type system (including `opaque` for arbitrary data)
|
* Strong, but optional type system
|
||||||
* Functions and types are first-class citizens
|
* First-class functions and closures
|
||||||
* Import native libraries from the host
|
* Extensible with native C-bindings
|
||||||
* Fancy slice notation for strings, arrays and dictionaries
|
* Can re-direct output, error and assertion messages
|
||||||
* Can re-direct output, error and assertion failure messages
|
* Open-Source under the zlib license
|
||||||
* Open source under the zlib license
|
|
||||||
|
|
||||||
## Building
|
# Syntax
|
||||||
|
|
||||||
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
|
```toy
|
||||||
|
fn makeCounter() {
|
||||||
|
var counter: Int = 0;
|
||||||
|
|
||||||
For Windows(MSVC), Visual Studio project files are included.
|
fn increment() {
|
||||||
|
return ++counter;
|
||||||
Note: MacOS and Windows(MSVC) are not officially supported, but we'll do our best!
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
Run `make install-tools` to install a number of tools, including:
|
|
||||||
|
|
||||||
* VSCode syntax highlighting
|
|
||||||
|
|
||||||
Other tools such as a disassembler are available, as well - simply run `make` in the correct directory.
|
|
||||||
|
|
||||||
## Syntax
|
|
||||||
|
|
||||||
```
|
|
||||||
import standard; //for a bunch of utility functions
|
|
||||||
|
|
||||||
print "Hello world"; //"print" is a keyword
|
|
||||||
|
|
||||||
var msg = "foobar"; //declare a variable like this
|
|
||||||
|
|
||||||
assert true, "This message won't be seen"; //assert is another keyword
|
|
||||||
|
|
||||||
//-------------------------
|
|
||||||
|
|
||||||
fn makeCounter() { //declare a function like this
|
|
||||||
var total: int = 0; //declare a variable with a type like this
|
|
||||||
|
|
||||||
fn counter(): int { //declare a return type like this
|
|
||||||
return ++total;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return counter; //closures are explicitly supported
|
return increment;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tally = makeCounter();
|
var tally = makeCounter();
|
||||||
|
|
||||||
print tally(); //1
|
while (true) {
|
||||||
print tally(); //2
|
var result = tally();
|
||||||
print tally(); //3
|
|
||||||
|
print result; //prints 1 to 10
|
||||||
|
|
||||||
|
if (result >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
This project requires `gcc` and `make` by default, but should also work in other environments. Officially supported platforms include `linux`, `windows` and `macOS`, see `source/toy_common.h` for implementation details.
|
||||||
|
|
||||||
|
Run `make` in the root directory to build the shared library named `libToy.so` and a useable REPL named `repl.out`.
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
The contents of `docs/` is also available on the official website [toylang.com](https://toylang.com/).
|
||||||
|
|
||||||
# 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](LICENSE) for details).
|
||||||
|
|
||||||
# Contributions
|
# Contributors and Special Thanks
|
||||||
|
|
||||||
@hiperiondev - Disassembler, porting support and feedback
|
@NishiOwO - Unofficial NetBSD support
|
||||||
@add00 - Library support
|
@Gipson62 - v1 docs spell checking
|
||||||
@gruelingpine185 - Unofficial MacOS support
|
@8051Enthusiast - `fixAlignment()` trick
|
||||||
@solar-mist - Minor bugfixes
|
@hiperiondev - v1 Disassembler, v1 porting support and feedback
|
||||||
Unnamed Individuals - Feedback
|
@add00 - v1 Library support
|
||||||
|
@gruelingpine185 - Unofficial v1 MacOS support
|
||||||
|
@solar-mist - v1 Minor bugfixes
|
||||||
|
Various Anons - Feedback
|
||||||
|
@munificent - For [writing the book](http://craftinginterpreters.com/) that sparked my interest in langdev
|
||||||
|
|
||||||
# Patrons via Patreon
|
# Patreon Supporters
|
||||||
|
|
||||||
* Seth A. Robinson
|
You can show your support and be listed here by joining my [Patreon](https://patreon.com/krgamestudios).
|
||||||
|
|
||||||
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
|
||||||
|
|||||||
-159
@@ -1,159 +0,0 @@
|
|||||||
<?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
@@ -1,184 +0,0 @@
|
|||||||
<?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
@@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
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
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[book]
|
||||||
|
title = "The Toy Programming Language"
|
||||||
|
authors = ["Kayne Ruse (Ratstail91)"]
|
||||||
|
description = "Documentation For The Toy Programming Language"
|
||||||
|
language = "en"
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
git-repository-url = "https://github.com/krgamestudios/Toy"
|
||||||
|
git-repository-icon = "fab-github"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# 404
|
||||||
|
|
||||||
|
Nobody here but us chickens!
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<p align="center">
|
||||||
|
<image src="img/toylogo.png" alt="The Toy Logo" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through external scripts.
|
||||||
|
|
||||||
|
## Nifty Features
|
||||||
|
|
||||||
|
* Simple C-like syntax
|
||||||
|
* Intermediate AST and bytecode representations
|
||||||
|
* Strong, but optional type system
|
||||||
|
* First-class functions and closures
|
||||||
|
* Extensible with native C-bindings
|
||||||
|
* Can re-direct output, error and assertion messages
|
||||||
|
* Open-Source under the zlib license
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```toy
|
||||||
|
fn makeCounter() {
|
||||||
|
var counter: Int = 0;
|
||||||
|
|
||||||
|
fn increment() {
|
||||||
|
return ++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tally = makeCounter();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var result = tally();
|
||||||
|
|
||||||
|
print result; //prints 1 to 10
|
||||||
|
|
||||||
|
if (result >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Further Reading
|
||||||
|
|
||||||
|
This website is a work in progress - for further info, see the official repository: [https://gitea.krgamestudios.com/krgamestudios/Toy](https://gitea.krgamestudios.com/krgamestudios/Toy), or the GitHub mirror: [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
|
||||||
|
|
||||||
|
An example of Toy in action: [Vampire Toyvivors](https://gitea.krgamestudios.com/krgamestudios/VampireToyvivors) (a simple "game" used for testing).
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
- [Front Page](./README.md)
|
||||||
|
- [Quick Start](./quickstart.md)
|
||||||
|
- [Cheat Sheet](./cheatsheet.md)
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
# Cheat Sheet
|
||||||
|
|
||||||
|
## Compile and Run A Snippet Of Code
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "toy_lexer.h"
|
||||||
|
#include "toy_parser.h"
|
||||||
|
#include "toy_compiler.h"
|
||||||
|
#include "toy_vm.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
//example code
|
||||||
|
const char* source = "print \"Hello world!\";";
|
||||||
|
|
||||||
|
//buckets use the arena pattern for memory allocation
|
||||||
|
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
|
||||||
|
//compile the code
|
||||||
|
Toy_Lexer lexer;
|
||||||
|
Toy_bindLexer(&lexer, (char*)source);
|
||||||
|
|
||||||
|
Toy_Parser parser;
|
||||||
|
Toy_bindParser(&parser, &lexer);
|
||||||
|
|
||||||
|
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||||
|
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||||
|
|
||||||
|
//the ast, which is stored in this bucket, is no longer needed
|
||||||
|
Toy_freeBucket(&bucket);
|
||||||
|
|
||||||
|
//the virtual machine used at runtime
|
||||||
|
Toy_VM vm;
|
||||||
|
Toy_initVM(&vm);
|
||||||
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
|
//execute the given code
|
||||||
|
Toy_runVM(&vm);
|
||||||
|
|
||||||
|
//cleanup after ourselves
|
||||||
|
Toy_freeVM(&vm);
|
||||||
|
free(bytecode);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick and Dirty Compilation
|
||||||
|
|
||||||
|
```c
|
||||||
|
unsigned char* compileSource(const char* source) {
|
||||||
|
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
|
||||||
|
Toy_Lexer lexer;
|
||||||
|
Toy_bindLexer(&lexer, source);
|
||||||
|
|
||||||
|
Toy_Parser parser;
|
||||||
|
Toy_bindParser(&parser, &lexer);
|
||||||
|
|
||||||
|
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||||
|
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||||
|
|
||||||
|
Toy_freeBucket(&bucket);
|
||||||
|
return bytecode;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Functions
|
||||||
|
|
||||||
|
This is a rough outline of all API functions declared in Toy's headers. As a rule, functions that begin with `TOY_API` are useable and begin with `Toy_`, while functions that begin with `Toy_private_` are generally not intended for use, and only exposed for technical reasons.
|
||||||
|
|
||||||
|
*Note: This list is updated manually, if something is outdated let me know.*
|
||||||
|
|
||||||
|
```c
|
||||||
|
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
|
||||||
|
TOY_API void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb);
|
||||||
|
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
|
||||||
|
TOY_API Toy_Bucket* Toy_allocateBucket(unsigned int capacity);
|
||||||
|
TOY_API unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount);
|
||||||
|
TOY_API void Toy_releaseBucketPartition(unsigned char* ptr);
|
||||||
|
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);
|
||||||
|
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
|
||||||
|
TOY_API unsigned char* Toy_compileToBytecode(Toy_Ast* ast);
|
||||||
|
TOY_API void Toy_freeFunction(Toy_Function* fn) {
|
||||||
|
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope);
|
||||||
|
TOY_API Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback);
|
||||||
|
TOY_API Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* fn);
|
||||||
|
TOY_API void Toy_freeFunction(Toy_Function* fn);
|
||||||
|
TOY_API void Toy_bindLexer(Toy_Lexer* lexer, const char* source);
|
||||||
|
TOY_API void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer);
|
||||||
|
TOY_API Toy_Ast* Toy_scanParser(Toy_Bucket** bucketHandle, Toy_Parser* parser);
|
||||||
|
TOY_API void Toy_resetParser(Toy_Parser* parser);
|
||||||
|
TOY_API void Toy_print(const char* msg);
|
||||||
|
TOY_API void Toy_error(const char* msg);
|
||||||
|
TOY_API void Toy_assertFailure(const char* msg);
|
||||||
|
TOY_API void Toy_setPrintCallback(Toy_callbackType cb);
|
||||||
|
TOY_API void Toy_setErrorCallback(Toy_callbackType cb);
|
||||||
|
TOY_API void Toy_setAssertFailureCallback(Toy_callbackType cb);
|
||||||
|
TOY_API void Toy_resetPrintCallback(void);
|
||||||
|
TOY_API void Toy_resetErrorCallback(void);
|
||||||
|
TOY_API void Toy_resetAssertFailureCallback(void);
|
||||||
|
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
|
||||||
|
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||||
|
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant);
|
||||||
|
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
|
||||||
|
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
|
||||||
|
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
||||||
|
TOY_API Toy_Stack* Toy_allocateStack(void);
|
||||||
|
TOY_API void Toy_freeStack(Toy_Stack* stack);
|
||||||
|
TOY_API void Toy_resetStack(Toy_Stack** stackHandle);
|
||||||
|
TOY_API void Toy_pushStack(Toy_Stack** stackHandle, Toy_Value value);
|
||||||
|
TOY_API Toy_Value Toy_peekStack(Toy_Stack** stackHandle);
|
||||||
|
TOY_API Toy_Value Toy_popStack(Toy_Stack** stackHandle);
|
||||||
|
TOY_API Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring);
|
||||||
|
TOY_API Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
|
||||||
|
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
|
||||||
|
TOY_API Toy_String* Toy_copyString(Toy_String* str);
|
||||||
|
TOY_API Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right);
|
||||||
|
TOY_API void Toy_freeString(Toy_String* str);
|
||||||
|
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
|
||||||
|
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
|
||||||
|
TOY_API char* Toy_getStringRaw(Toy_String* str);
|
||||||
|
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right);
|
||||||
|
TOY_API unsigned int Toy_hashString(Toy_String* string);
|
||||||
|
TOY_API Toy_Table* Toy_allocateTable(unsigned int minCapacity);
|
||||||
|
TOY_API void Toy_freeTable(Toy_Table* table);
|
||||||
|
TOY_API void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value);
|
||||||
|
TOY_API Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key);
|
||||||
|
TOY_API void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key);
|
||||||
|
TOY_API Toy_Value Toy_unwrapValue(Toy_Value value);
|
||||||
|
TOY_API unsigned int Toy_hashValue(Toy_Value value);
|
||||||
|
TOY_API Toy_Value Toy_copyValue(struct Toy_Bucket** bucketHandle, Toy_Value value);
|
||||||
|
TOY_API void Toy_freeValue(Toy_Value value);
|
||||||
|
TOY_API bool Toy_checkValueIsTruthy(Toy_Value value);
|
||||||
|
TOY_API bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right);
|
||||||
|
TOY_API bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right);
|
||||||
|
TOY_API int Toy_compareValues(Toy_Value left, Toy_Value right);
|
||||||
|
TOY_API union Toy_String_t* Toy_stringifyValue(struct Toy_Bucket** bucketHandle, Toy_Value value);
|
||||||
|
TOY_API const char* Toy_getValueTypeAsCString(Toy_ValueType type);
|
||||||
|
TOY_API void Toy_resetVM(Toy_VM* vm, bool preserveScope, bool preserveStack);
|
||||||
|
TOY_API void Toy_initVM(Toy_VM* vm);
|
||||||
|
TOY_API void Toy_inheritVM(Toy_VM* parentVM, Toy_VM* subVM);
|
||||||
|
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
|
||||||
|
TOY_API void Toy_freeVM(Toy_VM* vm);
|
||||||
|
TOY_API Toy_Value Toy_getReturnValueFromVM(Toy_VM* parentVM, Toy_VM* subVM);
|
||||||
|
```
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 454 KiB |
@@ -0,0 +1,186 @@
|
|||||||
|
# Toy v2 Quick-Start Guide
|
||||||
|
|
||||||
|
To help you start using Toy as fast as possible, here are the most useful elements of the language. Not everything available is listed, but this should let you start coding right away.
|
||||||
|
|
||||||
|
## Keyword 'print'
|
||||||
|
|
||||||
|
The `print` keyword prints a given value to stdout (or elsewhere if configured with the API).
|
||||||
|
|
||||||
|
```
|
||||||
|
print "Hello World!";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keyword 'assert'
|
||||||
|
|
||||||
|
The `assert` keyword takes two values separated by a comma. If the first value is falsy or `null` the optional second parameter is printed to stderr (or elsewhere if configured with the API). If no second parameter is provided a generic error message is used instead.
|
||||||
|
|
||||||
|
```
|
||||||
|
//nothing happens
|
||||||
|
assert 1 < 2;
|
||||||
|
|
||||||
|
//this assert will fail, and output the second parameter
|
||||||
|
assert null, "Hello world!";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables and Types
|
||||||
|
|
||||||
|
Variables are declared with the `var` keyword with and an optional type from the list below. If no type is specified `Any` is used instead.
|
||||||
|
|
||||||
|
```
|
||||||
|
var answer = 42;
|
||||||
|
|
||||||
|
var question: String = "How many roads must a man walk down?";
|
||||||
|
```
|
||||||
|
|
||||||
|
To make a variable immutable put the `const` keyword after the type. If you do, it must be assigned a value.
|
||||||
|
|
||||||
|
```
|
||||||
|
var quote: String const = "War. War never changes.";
|
||||||
|
```
|
||||||
|
|
||||||
|
Toy's types are:
|
||||||
|
|
||||||
|
| type | name | description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Bool` | Boolean | Either `true` or `false`. |
|
||||||
|
| `Int` | Integer | Any signed whole number (32-bits). |
|
||||||
|
| `Float` | Float | Any signed decimal number (32-bits), using floating point arithmatic. |
|
||||||
|
| `String` | String | Normal text, effectively utf-8. |
|
||||||
|
| `Array` | Array | A series of values stored sequentially in memory. |
|
||||||
|
| `Table` | Table | A series key-value pairs stored in a hash table. Booleans, functions, opaques and `null` can't be used as keys. |
|
||||||
|
| `Function` | Function | A chunk of reusable code that takes zero or more parameters, and may return a result. Functions are declared with the `fn` keyword, or in the API. |
|
||||||
|
| `Opaque` | Opaque | This value is unusable in Toy, but allows you to pass data between C bindings provided with the API. |
|
||||||
|
| `Any` | Any | The default type when nothing is specified. It can hold any value. |
|
||||||
|
|
||||||
|
## Control Flow
|
||||||
|
|
||||||
|
Making a decision, or repeating a chunk of code multiple times, is essential for any language. Choosing between multiple options can be done with the `if-then-else` statement - if the condition is truthy, the 'then-branch' will be executed. Otherwise, the optional 'else-branch' is executed instead.
|
||||||
|
|
||||||
|
```
|
||||||
|
var answer = 42;
|
||||||
|
|
||||||
|
if (answer < 56) {
|
||||||
|
print "Cod dang it!";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print "Something's fishy here...";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
var challenge = "hard";
|
||||||
|
|
||||||
|
if (challenge == "hard") {
|
||||||
|
print "I choose to build a scripting language, not because it's easy, but because it's hard!";
|
||||||
|
}
|
||||||
|
|
||||||
|
//the else-branch is optional
|
||||||
|
```
|
||||||
|
|
||||||
|
To repeat a certain action, use the `while-then` loop, which repeats the body as long as the given condition remains true on each loop.
|
||||||
|
|
||||||
|
```
|
||||||
|
var loops = 0;
|
||||||
|
|
||||||
|
while (loops++ < 8) {
|
||||||
|
print "These episodes are endless.";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To break out of a loop, you can use the `break` keyword. Alternatively, to restart the loop early, use the `continue` keyword.
|
||||||
|
|
||||||
|
```
|
||||||
|
var loops = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (++loops < 15532) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break; //poor yuki ;_;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Note: The `for` loop is coming soon, and will allow for iteration over an array or table, but isn't vital right now.*
|
||||||
|
|
||||||
|
## Arrays and Tables
|
||||||
|
|
||||||
|
Arrays are defined with a pair of brackets, and can contain a list of comma-separated values.
|
||||||
|
|
||||||
|
```
|
||||||
|
//define an array
|
||||||
|
var array = [1,2,3];
|
||||||
|
|
||||||
|
//specify the type
|
||||||
|
var bray: Array = [4,5,6];
|
||||||
|
|
||||||
|
//define an empty array
|
||||||
|
var craycray: Array = [];
|
||||||
|
|
||||||
|
//arrays are zero-indexed
|
||||||
|
print array[0]; //'1'
|
||||||
|
```
|
||||||
|
|
||||||
|
Tables are also defined with brackets, and contain a comma-separated list of key-value pairs defined by colons:
|
||||||
|
|
||||||
|
```
|
||||||
|
//most types can be used as keys
|
||||||
|
var table = ["alpha": 1, "beta": 2, "gamma": 3];
|
||||||
|
|
||||||
|
//the 'Table' keyword can define the type, and an empty table still has a colon
|
||||||
|
var under: Table = [:];
|
||||||
|
|
||||||
|
//printing the whole table does NOT guarantee internal order
|
||||||
|
print table["beta"];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
Some values, including Strings, Arrays and Tables, have "attributes" which are accessible with the dot `.` operator. These can expose internal values or components for manipulating said values.
|
||||||
|
|
||||||
|
```
|
||||||
|
var string = "Hello World";
|
||||||
|
print string.length; //11
|
||||||
|
print string.asUpper; //HELLO WORLD
|
||||||
|
print string.asLower; //hello world
|
||||||
|
|
||||||
|
var array = [1,2,3];
|
||||||
|
array.pushBack(4); //array = [1,2,3,4]
|
||||||
|
var element = array.popBack(); //element = 4
|
||||||
|
var emptyArray = [];
|
||||||
|
|
||||||
|
var table = ["alpha": 1, "beta":2];
|
||||||
|
print table.length; //2
|
||||||
|
table.insert("key",element); //table["key"] = 4
|
||||||
|
print table.hasKey("alpha"); //true
|
||||||
|
table.remove("alpha"); //table = ["beta":2,"key":4]
|
||||||
|
var emptyTable = [:];
|
||||||
|
```
|
||||||
|
|
||||||
|
Opaques can also be given attributes, but this requires some in-depth understanding of the API, so won't be covered here.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
Functions are defined with the `fn` keyword, and follow a c-like syntax, with optional types on each parameter:
|
||||||
|
|
||||||
|
```toy
|
||||||
|
fn fib(n: Int) {
|
||||||
|
if (n < 2) return n;
|
||||||
|
return fib(n-1) + fib(n-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
print fib(12); //144
|
||||||
|
```
|
||||||
|
|
||||||
|
```toy
|
||||||
|
fn isLeapYear(n: Int) {
|
||||||
|
if (n % 400 == 0) return true;
|
||||||
|
if (n % 100 == 0) return false;
|
||||||
|
return n % 4 == 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## External API and Extending Toy
|
||||||
|
|
||||||
|
*Note: Watch this space, docs for the C API are coming soon. For now, the [Cheat Sheet](/cheatsheet) can get you started.*
|
||||||
|
|
||||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<!-- open graph protocol -->
|
||||||
|
<meta property="og:url" content="{{ url }}" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:image" content="{{ base_url }}/img/toypreview.png" />
|
||||||
|
<meta property="og:title" content="{{ title }}" />
|
||||||
|
<meta property="og:description" content="{{ description }}" />
|
||||||
|
|
||||||
|
<!-- twitter has to be special -->
|
||||||
|
<meta name="twitter:card" content="{{ title }}" />
|
||||||
|
<meta name="twitter:url" content="{{ url}}" />
|
||||||
|
<meta name="twitter:type" content="website" />
|
||||||
|
<meta name="twitter:image" content="{{ base_url }}/img/toypreview.png" />
|
||||||
|
<meta name="twitter:title" content="{{ title }}" />
|
||||||
|
<meta name="twitter:description" content="{{ description }}" />
|
||||||
@@ -1,109 +1,83 @@
|
|||||||
export CFLAGS+=-std=c18 -pedantic -Werror
|
#compiler settings reference
|
||||||
|
#CC=gcc
|
||||||
|
#CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
|
||||||
|
#LIBS+=-lm
|
||||||
|
#LDFLAGS+=
|
||||||
|
|
||||||
|
#TODO: release builds should define the NDEBUG flag; double check it works
|
||||||
|
|
||||||
|
#directories
|
||||||
|
export TOY_SOURCEDIR=source
|
||||||
|
export TOY_REPLDIR=repl
|
||||||
export TOY_OUTDIR=out
|
export TOY_OUTDIR=out
|
||||||
|
export TOY_OBJDIR=obj
|
||||||
|
|
||||||
all: $(TOY_OUTDIR) repl
|
#targets
|
||||||
|
all: source repl
|
||||||
|
|
||||||
#repl builds
|
.PHONY: source
|
||||||
repl: $(TOY_OUTDIR) library
|
source:
|
||||||
$(MAKE) -j8 -C repl
|
$(MAKE) -C source -k
|
||||||
|
|
||||||
repl-static: $(TOY_OUTDIR) static
|
.PHONY: repl
|
||||||
$(MAKE) -j8 -C repl
|
repl: source
|
||||||
|
$(MAKE) -C repl -k
|
||||||
|
|
||||||
repl-release: clean $(TOY_OUTDIR) library-release
|
.PHONY: tests tests-ci
|
||||||
$(MAKE) -C repl release
|
tests: clean
|
||||||
|
$(MAKE) -C tests -k
|
||||||
|
|
||||||
repl-static-release: clean $(TOY_OUTDIR) static-release
|
tests-gdb: clean
|
||||||
$(MAKE) -C repl release
|
$(MAKE) -C tests -k gdb
|
||||||
|
|
||||||
#lib builds
|
|
||||||
library: $(TOY_OUTDIR)
|
|
||||||
$(MAKE) -j8 -C source library
|
|
||||||
|
|
||||||
static: $(TOY_OUTDIR)
|
|
||||||
$(MAKE) -j8 -C source static
|
|
||||||
|
|
||||||
library-release: clean $(TOY_OUTDIR)
|
|
||||||
$(MAKE) -j8 -C source library-release
|
|
||||||
|
|
||||||
static-release: clean $(TOY_OUTDIR)
|
|
||||||
$(MAKE) -j8 -C source static-release
|
|
||||||
|
|
||||||
#distribution
|
|
||||||
dist: export CFLAGS+=-O2 -mtune=native -march=native
|
|
||||||
dist: repl-release
|
|
||||||
|
|
||||||
#utils
|
|
||||||
test: clean $(TOY_OUTDIR)
|
|
||||||
$(MAKE) -C test
|
|
||||||
|
|
||||||
test-sanitized: export CFLAGS+=-fsanitize=address,undefined
|
|
||||||
test-sanitized: export LIBS+=-static-libasan
|
|
||||||
test-sanitized: export DISABLE_VALGRIND=true
|
|
||||||
test-sanitized: clean $(TOY_OUTDIR)
|
|
||||||
$(MAKE) -C test
|
|
||||||
|
|
||||||
|
#util targets
|
||||||
$(TOY_OUTDIR):
|
$(TOY_OUTDIR):
|
||||||
mkdir $(TOY_OUTDIR)
|
mkdir $(TOY_OUTDIR)
|
||||||
|
|
||||||
#utils
|
$(TOY_OBJDIR):
|
||||||
install-tools:
|
mkdir $(TOY_OBJDIR)
|
||||||
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
|
|
||||||
|
|
||||||
|
#util commands
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
ifeq ($(shell uname),Linux)
|
||||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
find . -type f -name '*.o' -delete
|
||||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
find . -type f -name '*.a' -delete
|
||||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
find . -type f -name '*.out' -delete
|
||||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
find . -type f -name '*.exe' -delete
|
||||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
find . -type f -name '*.dll' -delete
|
||||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
find . -type f -name '*.lib' -delete
|
||||||
find . -empty -type d -delete
|
find . -type f -name '*.so' -delete
|
||||||
else ifeq ($(shell uname),Linux)
|
find . -type f -name '*.dylib' -delete
|
||||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
find . -type d -name 'out' -delete
|
||||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
find . -type d -name 'obj' -delete
|
||||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
else ifeq ($(shell uname),NetBSD)
|
||||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
find . -type f -name '*.o' -delete
|
||||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
find . -type f -name '*.a' -delete
|
||||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
find . -type f -name '*.out' -delete
|
||||||
rm -rf out
|
find . -type f -name '*.exe' -delete
|
||||||
find . -empty -type d -delete
|
find . -type f -name '*.dll' -delete
|
||||||
|
find . -type f -name '*.lib' -delete
|
||||||
|
find . -type f -name '*.so' -delete
|
||||||
|
find . -type f -name '*.dylib' -delete
|
||||||
|
find . -type d -name 'out' -delete
|
||||||
|
find . -type d -name 'obj' -delete
|
||||||
else ifeq ($(OS),Windows_NT)
|
else ifeq ($(OS),Windows_NT)
|
||||||
$(RM) *.o *.a *.exe
|
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib
|
||||||
|
$(RM) out
|
||||||
|
$(RM) obj
|
||||||
else ifeq ($(shell uname),Darwin)
|
else ifeq ($(shell uname),Darwin)
|
||||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
find . -type f -name '*.o' -delete
|
||||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
find . -type f -name '*.a' -delete
|
||||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
find . -type f -name '*.out' -delete
|
||||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
find . -type f -name '*.exe' -delete
|
||||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
find . -type f -name '*.dll' -delete
|
||||||
find . -type f -name '*.dylib' -exec rm -f -r -v {} \;
|
find . -type f -name '*.lib' -delete
|
||||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
find . -type f -name '*.so' -delete
|
||||||
rm -rf out
|
find . -type f -name '*.dylib' -delete
|
||||||
find . -empty -type d -delete
|
find . -type d -name 'out' -delete
|
||||||
|
find . -type d -name 'obj' -delete
|
||||||
else
|
else
|
||||||
@echo "Deletion failed - what platform is this?"
|
@echo "Deletion failed - what platform is this?"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
rebuild: clean all
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
#include "ast_inspector.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
#include "toy_bucket.h"
|
||||||
|
#include "toy_string.h"
|
||||||
|
#include "toy_value.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void inspect_by_type(Toy_Ast* ast, int depth);
|
||||||
|
|
||||||
|
void inspect_block(Toy_Ast* ast, int depth);
|
||||||
|
void inspect_value(Toy_Ast* ast, int depth);
|
||||||
|
void inspect_print(Toy_Ast* ast, int depth);
|
||||||
|
|
||||||
|
#define PRINTSTR(x) printf("%*s%s", depth*4, "", x);
|
||||||
|
|
||||||
|
static Toy_Bucket* bucket = NULL; //lazy
|
||||||
|
|
||||||
|
void inspect_ast(Toy_Ast* ast) {
|
||||||
|
bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
inspect_by_type(ast, 0);
|
||||||
|
Toy_freeBucket(&bucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inspect_by_type(Toy_Ast* ast, int depth) {
|
||||||
|
switch(ast->type) {
|
||||||
|
case TOY_AST_BLOCK:
|
||||||
|
inspect_block(ast, depth);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TOY_AST_VALUE:
|
||||||
|
inspect_value(ast, depth);
|
||||||
|
return;
|
||||||
|
// case TOY_AST_UNARY:
|
||||||
|
// case TOY_AST_BINARY:
|
||||||
|
// case TOY_AST_BINARY_SHORT_CIRCUIT:
|
||||||
|
// case TOY_AST_COMPARE:
|
||||||
|
// case TOY_AST_GROUP:
|
||||||
|
// case TOY_AST_COMPOUND:
|
||||||
|
// case TOY_AST_AGGREGATE:
|
||||||
|
|
||||||
|
// case TOY_AST_ASSERT:
|
||||||
|
// case TOY_AST_IF_THEN_ELSE:
|
||||||
|
// case TOY_AST_WHILE_THEN:
|
||||||
|
// case TOY_AST_BREAK:
|
||||||
|
// case TOY_AST_CONTINUE:
|
||||||
|
// case TOY_AST_RETURN:
|
||||||
|
case TOY_AST_PRINT:
|
||||||
|
inspect_print(ast, depth);
|
||||||
|
return;
|
||||||
|
|
||||||
|
// case TOY_AST_VAR_DECLARE:
|
||||||
|
// case TOY_AST_VAR_ASSIGN:
|
||||||
|
// case TOY_AST_VAR_ACCESS:
|
||||||
|
|
||||||
|
// case TOY_AST_FN_DECLARE:
|
||||||
|
// case TOY_AST_FN_INVOKE:
|
||||||
|
|
||||||
|
// case TOY_AST_STACK_POP:
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf(TOY_CC_WARN "%*sAST %s (unhandled by inspector)" TOY_CC_RESET "\n", depth*4, "", Toy_private_getAstTypeAsCString(ast->type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inspect_block(Toy_Ast* ast, int depth) {
|
||||||
|
//show the block braces
|
||||||
|
PRINTSTR("{\n");
|
||||||
|
|
||||||
|
if (ast->block.child) {
|
||||||
|
inspect_by_type(ast->block.child, depth + 1);
|
||||||
|
|
||||||
|
if (ast->block.next) {
|
||||||
|
inspect_block(ast->block.next, depth + 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTSTR("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void inspect_value(Toy_Ast* ast, int depth) {
|
||||||
|
(void)depth;
|
||||||
|
Toy_String* str = Toy_stringifyValue(&bucket, ast->value.value);
|
||||||
|
|
||||||
|
char* buffer = Toy_getStringRaw(str); //SLOW
|
||||||
|
printf("%s '%s'", Toy_getValueTypeAsCString(ast->value.value.type), buffer);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
Toy_freeString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inspect_print(Toy_Ast* ast, int depth) {
|
||||||
|
(void)depth;
|
||||||
|
PRINTSTR("PRINT ");
|
||||||
|
|
||||||
|
inspect_by_type(ast->print.child, depth);
|
||||||
|
|
||||||
|
printf(";\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_ast.h"
|
||||||
|
|
||||||
|
void inspect_ast(Toy_Ast* astHandle);
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#include "bucket_inspector.h"
|
||||||
|
#include <toy_string.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int inspect_bucket(Toy_Bucket** bucketHandle) {
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
//for each bucket
|
||||||
|
for (Toy_Bucket* iter = (*bucketHandle); iter != NULL; iter = iter->next) {
|
||||||
|
int occupied = 0;
|
||||||
|
int released = 0;
|
||||||
|
unsigned char* ptr = iter->data;
|
||||||
|
|
||||||
|
|
||||||
|
while ((ptr - iter->data < iter->count) && *((int*)ptr) != 0) { //for each partition
|
||||||
|
if ( ( *((int*)ptr) & 1) == 0) { //is this partition still in use?
|
||||||
|
occupied++;
|
||||||
|
|
||||||
|
//try to print as a string if possible
|
||||||
|
Toy_String* str = (void*)(ptr + 4);
|
||||||
|
|
||||||
|
if (str->info.type == TOY_STRING_LEAF && str->info.length < 255) {
|
||||||
|
printf("String Leaf (%d bytes, %d refCount): %.*s\n", *((int*)ptr), str->info.refCount, str->info.length, str->leaf.data);
|
||||||
|
}
|
||||||
|
else if (str->info.type == TOY_STRING_NODE) {
|
||||||
|
printf("String Node (%d bytes, %d refCount): ...\n", *((int*)ptr), str->info.refCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
released++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//jump distance: ((*((int*)ptr) | 1) ^ 1) + 4
|
||||||
|
// printf(" jump %d, ", ((*((int*)ptr) | 1) ^ 1) + 4);
|
||||||
|
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Bucket link %d: count %u, %d occupied, %d released\n", depth, iter->count, occupied, released);
|
||||||
|
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_bucket.h"
|
||||||
|
|
||||||
|
int inspect_bucket(Toy_Bucket** bucketHandle);
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
#include "bytecode_inspector.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
#include "toy_opcodes.h"
|
||||||
|
#include "toy_value.h"
|
||||||
|
#include "toy_string.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr);
|
||||||
|
int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr);
|
||||||
|
|
||||||
|
#define ISPRINT_SANITIZE(x) (isprint((int)x) > 0 ? (x) : '_')
|
||||||
|
|
||||||
|
#define MARKER_VALUE(pc, type) \
|
||||||
|
((unsigned int)(pc * sizeof(type)))
|
||||||
|
|
||||||
|
#define MARKER "\t\033[" TOY_CC_FONT_BLACK "m" " %u\t" TOY_CC_RESET
|
||||||
|
#define FONT_BLACK "\033[" TOY_CC_FONT_BLACK "m"
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
int inspect_bytecode(unsigned char* bytecode) {
|
||||||
|
//TODO: handle version info
|
||||||
|
|
||||||
|
unsigned int const bytecodeSize = ((unsigned int*)(bytecode))[0];
|
||||||
|
unsigned int const jumpsSize = ((unsigned int*)(bytecode))[1];
|
||||||
|
unsigned int const paramSize = ((unsigned int*)(bytecode))[2];
|
||||||
|
unsigned int const dataSize = ((unsigned int*)(bytecode))[3];
|
||||||
|
unsigned int const subsSize = ((unsigned int*)(bytecode))[4];
|
||||||
|
|
||||||
|
printf(FONT_BLACK ".header:\r" TOY_CC_RESET);
|
||||||
|
|
||||||
|
//bytecode size
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Bytecode Size: \t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(0, unsigned int), bytecodeSize);
|
||||||
|
|
||||||
|
//header counts
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Jumps Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(1, unsigned int), jumpsSize);
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Param Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(2, unsigned int), paramSize);
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Data Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(3, unsigned int), dataSize);
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Subs Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(4, unsigned int), subsSize);
|
||||||
|
|
||||||
|
//some addresses may be absent
|
||||||
|
unsigned int addr_pc = 4;
|
||||||
|
unsigned int code_addr = 0;
|
||||||
|
unsigned int jumps_addr = 0;
|
||||||
|
unsigned int param_addr = 0;
|
||||||
|
unsigned int data_addr = 0;
|
||||||
|
unsigned int subs_addr = 0;
|
||||||
|
|
||||||
|
//bugfix
|
||||||
|
unsigned int code_end = 0;
|
||||||
|
|
||||||
|
//header addresses
|
||||||
|
if (true) {
|
||||||
|
addr_pc++;
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Code Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
|
||||||
|
code_addr = ((unsigned int*)(bytecode))[addr_pc];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jumpsSize > 0) {
|
||||||
|
addr_pc++;
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Jumps Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
|
||||||
|
jumps_addr = ((unsigned int*)(bytecode))[addr_pc];
|
||||||
|
if (code_end == 0) code_end = jumps_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paramSize > 0) {
|
||||||
|
addr_pc++;
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Param Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
|
||||||
|
param_addr = ((unsigned int*)(bytecode))[addr_pc];
|
||||||
|
if (code_end == 0) code_end = param_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataSize > 0) {
|
||||||
|
addr_pc++;
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Data Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
|
||||||
|
data_addr = ((unsigned int*)(bytecode))[addr_pc];
|
||||||
|
if (code_end == 0) code_end = data_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subsSize > 0) {
|
||||||
|
addr_pc++;
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Subs Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
|
||||||
|
subs_addr = ((unsigned int*)(bytecode))[addr_pc];
|
||||||
|
if (code_end == 0) code_end = subs_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code_end == 0) code_end = bytecodeSize; //very hacky
|
||||||
|
|
||||||
|
printf(FONT_BLACK ".code:\r" TOY_CC_RESET);
|
||||||
|
|
||||||
|
unsigned int pc = code_addr;
|
||||||
|
while(pc < code_end) {
|
||||||
|
pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//jumps
|
||||||
|
if (jumpsSize > 0) {
|
||||||
|
printf(FONT_BLACK ".jumps:\r" TOY_CC_RESET);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; (i*4) < jumpsSize; i++) {
|
||||||
|
printf(MARKER TOY_CC_NOTICE "%u (data %u)" TOY_CC_RESET "\n", MARKER_VALUE(jumps_addr + i, unsigned int),
|
||||||
|
i,
|
||||||
|
((unsigned int*)(bytecode + jumps_addr))[i] + data_addr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//param
|
||||||
|
if (paramSize > 0) {
|
||||||
|
printf(FONT_BLACK ".param:\r" TOY_CC_RESET);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; (i*4) < paramSize; i += 2) {
|
||||||
|
printf(MARKER TOY_CC_NOTICE "%u (type %s, data %u)" TOY_CC_RESET "\n", MARKER_VALUE(param_addr + i, unsigned int),
|
||||||
|
i,
|
||||||
|
Toy_getValueTypeAsCString(((unsigned int*)(bytecode + param_addr))[i + 1]),
|
||||||
|
((unsigned int*)(bytecode + param_addr))[i] + data_addr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//data; assume there's only strings for now
|
||||||
|
if (dataSize > 0) {
|
||||||
|
printf(FONT_BLACK ".data:\r" TOY_CC_RESET);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; (i*4) < dataSize; i++) {
|
||||||
|
printf(MARKER TOY_CC_NOTICE "%c %c %c %c" TOY_CC_RESET "\n", MARKER_VALUE(data_addr + i, unsigned int),
|
||||||
|
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[0]),
|
||||||
|
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[1]),
|
||||||
|
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[2]),
|
||||||
|
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[3])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//subs
|
||||||
|
if (subsSize > 0) {
|
||||||
|
printf(FONT_BLACK ".subs:\n" TOY_CC_RESET);
|
||||||
|
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (i < subsSize) {
|
||||||
|
i += inspect_bytecode(bytecode + subs_addr + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytecodeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr) {
|
||||||
|
//read and print the opcode instruction at 'pc'
|
||||||
|
|
||||||
|
Toy_OpcodeType opcode = bytecode[pc];
|
||||||
|
|
||||||
|
switch(opcode) {
|
||||||
|
case TOY_OPCODE_READ:
|
||||||
|
return inspect_read(bytecode, pc, jumps_addr, data_addr);
|
||||||
|
|
||||||
|
case TOY_OPCODE_DECLARE: {
|
||||||
|
unsigned int indexValue = *((unsigned int*)(bytecode + pc + 4));
|
||||||
|
unsigned int jumpValue = *((unsigned int*)(bytecode + jumps_addr + indexValue));
|
||||||
|
char* cstr = ((char*)(bytecode + data_addr + jumpValue));
|
||||||
|
printf(MARKER "DECLARE %s: %s%s\n", MARKER_VALUE(pc, unsigned char),
|
||||||
|
cstr,
|
||||||
|
Toy_getValueTypeAsCString(bytecode[pc + 1]),
|
||||||
|
bytecode[pc + 3] ? " const" : ""
|
||||||
|
);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_OPCODE_ASSIGN:
|
||||||
|
printf(MARKER "ASSIGN %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "(chained)" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ASSIGN_COMPOUND:
|
||||||
|
printf(MARKER "ASSIGN COMPOUND %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "(chained)" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ACCESS:
|
||||||
|
printf(MARKER "ACCESS\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_INVOKE:
|
||||||
|
printf(MARKER "INVOKE as '%s' (%d parameters)\n", MARKER_VALUE(pc, unsigned char),
|
||||||
|
Toy_getValueTypeAsCString(bytecode[pc + 1]),
|
||||||
|
bytecode[pc + 2]);
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ATTRIBUTE:
|
||||||
|
printf(MARKER "ATTRIBUTE (accessed from a compound)\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_DUPLICATE:
|
||||||
|
printf(MARKER "DUPLICATE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "and ACCESS" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ELIMINATE:
|
||||||
|
printf(MARKER "ELIMINATE\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ADD:
|
||||||
|
printf(MARKER "ADD %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_SUBTRACT:
|
||||||
|
printf(MARKER "SUBTRACT %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_MULTIPLY:
|
||||||
|
printf(MARKER "MULTIPLY %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_DIVIDE:
|
||||||
|
printf(MARKER "DIVIDE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_MODULO:
|
||||||
|
printf(MARKER "MODULO %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_COMPARE_EQUAL:
|
||||||
|
printf(MARKER "COMPARE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] != TOY_OPCODE_NEGATE ? "==" : "!=");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_COMPARE_LESS:
|
||||||
|
printf(MARKER "COMPARE '<'\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_COMPARE_LESS_EQUAL:
|
||||||
|
printf(MARKER "COMPARE '<='\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_COMPARE_GREATER:
|
||||||
|
printf(MARKER "COMPARE '>'\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_COMPARE_GREATER_EQUAL:
|
||||||
|
printf(MARKER "COMPARE '>='\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_AND:
|
||||||
|
printf(MARKER "LOGICAL '&&'\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_OR:
|
||||||
|
printf(MARKER "LOGICAL '||'\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_TRUTHY:
|
||||||
|
printf(MARKER "LOGICAL TRUTHY\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_NEGATE:
|
||||||
|
printf(MARKER "LOGICAL NEGATE\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_RETURN:
|
||||||
|
printf(MARKER "Keyword RETURN (%u)\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1]);
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_JUMP:
|
||||||
|
printf(MARKER TOY_CC_DEBUG "JUMP %s%s (%s%d) (GOTO %u)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
|
||||||
|
bytecode[pc + 1] == TOY_OP_PARAM_JUMP_ABSOLUTE ? "absolute" : "relative",
|
||||||
|
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_ALWAYS ? "" :
|
||||||
|
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_TRUE ? " if true" : " if false",
|
||||||
|
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
|
||||||
|
bytecode[pc + 4],
|
||||||
|
bytecode[pc + 4] + pc + 8
|
||||||
|
);
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ESCAPE:
|
||||||
|
printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
|
||||||
|
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
|
||||||
|
bytecode[pc + 4],
|
||||||
|
bytecode[pc + 4] + pc + 12,
|
||||||
|
bytecode[pc + 8]
|
||||||
|
);
|
||||||
|
return 12;
|
||||||
|
|
||||||
|
case TOY_OPCODE_SCOPE_PUSH:
|
||||||
|
printf(MARKER "SCOPE PUSH\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_SCOPE_POP:
|
||||||
|
printf(MARKER "SCOPE POP\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_ASSERT:
|
||||||
|
printf(MARKER TOY_CC_WARN "Keyword ASSERT (cond%s)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] > 1 ? ",msg" : "");
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_PRINT:
|
||||||
|
printf(MARKER TOY_CC_NOTICE "Keyword PRINT\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_CONCAT:
|
||||||
|
printf(MARKER "CONCATENATE strings\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case TOY_OPCODE_INDEX:
|
||||||
|
printf(MARKER "INDEX (%d elements)\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1]);
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
// case TOY_OPCODE_UNUSED:
|
||||||
|
// case TOY_OPCODE_PASS:
|
||||||
|
// case TOY_OPCODE_ERROR:
|
||||||
|
// case TOY_OPCODE_EOF:
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf(MARKER TOY_CC_WARN "Unknown Word: [%u, %u, %u, %u]" TOY_CC_RESET "\n", MARKER_VALUE(pc, unsigned char), bytecode[pc], bytecode[pc+1], bytecode[pc+2], bytecode[pc+3]);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr) {
|
||||||
|
Toy_ValueType type = bytecode[pc + 1];
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case TOY_VALUE_NULL: {
|
||||||
|
printf(MARKER "READ NULL\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_BOOLEAN: {
|
||||||
|
if (bytecode[pc + 2]) {
|
||||||
|
printf(MARKER "READ BOOL true\n", MARKER_VALUE(pc, unsigned char));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
}
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_INTEGER: {
|
||||||
|
int i = *(int*)(bytecode + pc + 4);
|
||||||
|
printf(MARKER "READ INTEGER %d\n", MARKER_VALUE(pc, unsigned char), i);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_FLOAT: {
|
||||||
|
float i = *(float*)(bytecode + pc + 4);
|
||||||
|
printf(MARKER "READ FLOAT %f\n", MARKER_VALUE(pc, unsigned char), i);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_STRING: {
|
||||||
|
Toy_StringType stringType = (Toy_StringType)(*(bytecode + pc + 2)); //Probably not needed
|
||||||
|
int len = bytecode[pc + 3]; //only used for names?
|
||||||
|
(void)len;
|
||||||
|
|
||||||
|
(void)stringType;
|
||||||
|
|
||||||
|
unsigned int indexValue = *((unsigned int*)(bytecode + pc + 4));
|
||||||
|
unsigned int jumpValue = *((unsigned int*)(bytecode + jumps_addr + indexValue));
|
||||||
|
char* cstr = ((char*)(bytecode + data_addr + jumpValue));
|
||||||
|
|
||||||
|
printf(MARKER "READ STRING [%u] '%s'\n", MARKER_VALUE(pc, unsigned char), indexValue, cstr);
|
||||||
|
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_FUNCTION:
|
||||||
|
printf(MARKER "READ FUNCTION '%u' (%d params)\n", MARKER_VALUE(pc, unsigned char), *((unsigned int*)(bytecode + pc + 4)), bytecode[pc + 2]);
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
case TOY_VALUE_ARRAY: {
|
||||||
|
unsigned int count = *((unsigned int*)(bytecode + pc + 4));
|
||||||
|
printf(MARKER "READ ARRAY %u elements\n", MARKER_VALUE(pc, unsigned char), count);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_TABLE: {
|
||||||
|
unsigned int count = *((unsigned int*)(bytecode + pc + 4));
|
||||||
|
printf(MARKER "READ TABLE %u elements (consuming %u values)\n", MARKER_VALUE(pc, unsigned char), count / 2, count);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOY_VALUE_OPAQUE:
|
||||||
|
case TOY_VALUE_ANY:
|
||||||
|
case TOY_VALUE_UNKNOWN:
|
||||||
|
default: {
|
||||||
|
printf(MARKER TOY_CC_WARN "READ %s (unhandled by inspector)" TOY_CC_RESET "\n", MARKER_VALUE(pc, unsigned char), Toy_getValueTypeAsCString(type));
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
int inspect_bytecode(unsigned char* bytecode);
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#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
@@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
int Toy_hookMath(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
#define TOY_OPAQUE_TAG_RANDOM 200
|
|
||||||
|
|
||||||
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
@@ -1,511 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
#define TOY_OPAQUE_TAG_RUNNER 100
|
|
||||||
|
|
||||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
-2223
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
|
||||||
+532
@@ -0,0 +1,532 @@
|
|||||||
|
#include "ast_inspector.h"
|
||||||
|
#include "bytecode_inspector.h"
|
||||||
|
#include "bucket_inspector.h"
|
||||||
|
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include "toy_lexer.h"
|
||||||
|
#include "toy_parser.h"
|
||||||
|
#include "toy_compiler.h"
|
||||||
|
#include "toy_vm.h"
|
||||||
|
|
||||||
|
//NOTE: for testing
|
||||||
|
#include "standard_library.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
unsigned char* readFile(char* path, int* size) {
|
||||||
|
//open the file
|
||||||
|
FILE* file = fopen(path, "rb");
|
||||||
|
if (file == NULL) {
|
||||||
|
*size = -1; //missing file error
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//determine the file's length
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
*size = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
//make some space
|
||||||
|
unsigned char* buffer = malloc(*size + 1);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//read the file
|
||||||
|
if (fread(buffer, sizeof(unsigned char), *size, file) < (unsigned int)(*size)) {
|
||||||
|
fclose(file);
|
||||||
|
free(buffer);
|
||||||
|
*size = -2; //singal a read error
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[(*size)++] = '\0';
|
||||||
|
|
||||||
|
//clean up and return
|
||||||
|
fclose(file);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getFileName(char* dest, const char* src, size_t destLength) {
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
char* p = NULL;
|
||||||
|
|
||||||
|
//find the last slash, regardless of platform
|
||||||
|
p = strrchr(src, '\\');
|
||||||
|
if (p == NULL) {
|
||||||
|
p = strrchr(src, '/');
|
||||||
|
}
|
||||||
|
if (p == NULL) {
|
||||||
|
int len = MIN(strlen(src), destLength-1);
|
||||||
|
strncpy(dest, src, len);
|
||||||
|
dest[len] = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
p++; //skip the slash
|
||||||
|
|
||||||
|
//determine length of the file name
|
||||||
|
int len = MIN(strlen(src), destLength-1);
|
||||||
|
|
||||||
|
//copy to the dest
|
||||||
|
strncpy(dest, p, len);
|
||||||
|
dest[len] = '\0';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
#undef MIN
|
||||||
|
}
|
||||||
|
|
||||||
|
//error callbacks
|
||||||
|
static int errorAndExitCallback(const char* msg) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int errorAndContinueCallback(const char* msg) {
|
||||||
|
return fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int assertFailureAndExitCallback(const char* msg) {
|
||||||
|
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int assertFailureAndContinueCallback(const char* msg) {
|
||||||
|
return fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int noOpCallback(const char* msg) {
|
||||||
|
//NO-OP
|
||||||
|
(void)msg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int silentExitCallback(const char* msg) {
|
||||||
|
//NO-OP
|
||||||
|
(void)msg;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle command line arguments
|
||||||
|
typedef struct CmdLine {
|
||||||
|
bool error;
|
||||||
|
bool help;
|
||||||
|
bool version;
|
||||||
|
char* infile;
|
||||||
|
int infileLength;
|
||||||
|
bool silentPrint;
|
||||||
|
bool silentAssert;
|
||||||
|
bool removeAssert;
|
||||||
|
bool verbose;
|
||||||
|
} CmdLine;
|
||||||
|
|
||||||
|
void usageCmdLine(int argc, const char* argv[]) {
|
||||||
|
(void)argc;
|
||||||
|
printf("Usage: %s [ -h | -v | -f source.toy ]\n\n", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helpCmdLine(int argc, const char* argv[]) {
|
||||||
|
usageCmdLine(argc, argv);
|
||||||
|
|
||||||
|
printf("The Toy Programming Language, leave arguments blank for an interactive REPL.\n\n");
|
||||||
|
|
||||||
|
printf(" -h, --help\t\t\tShow this help then exit.\n");
|
||||||
|
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
|
||||||
|
printf(" -f, --file infile\t\tParse, compile and execute the source file then exit.\n");
|
||||||
|
printf(" --silent-print\t\tSuppress output from the print keyword.\n");
|
||||||
|
printf(" --silent-assert\t\tSuppress output from the assert keyword.\n");
|
||||||
|
printf(" --remove-assert\t\tDo not include the assert statement in the bytecode.\n");
|
||||||
|
printf(" -d, --verbose\t\tPrint debugging information about Toy's internals.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void versionCmdLine(int argc, const char* argv[]) {
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
printf("The Toy Programming Language, Version %d.%d.%d %s\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||||
|
|
||||||
|
//copy/pasted from the license file - there's a way to include it directly, but it's too finnicky to bother
|
||||||
|
const char* license =
|
||||||
|
"Copyright (c) 2020-2026 Kayne Ruse, KR Game Studios\n"
|
||||||
|
"\n"
|
||||||
|
"This software is provided 'as-is', without any express or implied\n"
|
||||||
|
"warranty. In no event will the authors be held liable for any damages\n"
|
||||||
|
"arising from the use of this software.\n"
|
||||||
|
"\n"
|
||||||
|
"Permission is granted to anyone to use this software for any purpose,\n"
|
||||||
|
"including commercial applications, and to alter it and redistribute it\n"
|
||||||
|
"freely, subject to the following restrictions:\n"
|
||||||
|
"\n"
|
||||||
|
"1. The origin of this software must not be misrepresented; you must not\n"
|
||||||
|
"claim that you wrote the original software. If you use this software\n"
|
||||||
|
"in a product, an acknowledgment in the product documentation would be\n"
|
||||||
|
"appreciated but is not required.\n"
|
||||||
|
"2. Altered source versions must be plainly marked as such, and must not be\n"
|
||||||
|
"misrepresented as being the original software.\n"
|
||||||
|
"3. This notice may not be removed or altered from any source distribution.\n"
|
||||||
|
"\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
printf("%s",license);
|
||||||
|
}
|
||||||
|
|
||||||
|
CmdLine parseCmdLine(int argc, const char* argv[]) {
|
||||||
|
CmdLine cmd = {
|
||||||
|
.error = false,
|
||||||
|
.help = false,
|
||||||
|
.version = false,
|
||||||
|
.infile = NULL,
|
||||||
|
.infileLength = 0,
|
||||||
|
.silentPrint = false,
|
||||||
|
.silentAssert = false,
|
||||||
|
.removeAssert = false,
|
||||||
|
.verbose = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||||
|
cmd.help = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||||
|
cmd.version = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) {
|
||||||
|
if (argc <= i + 1) {
|
||||||
|
cmd.error = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cmd.infile != NULL) { //don't leak
|
||||||
|
free(cmd.infile);
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
//total space to reserve
|
||||||
|
cmd.infileLength = strlen(argv[i]) + 1;
|
||||||
|
cmd.infileLength = (cmd.infileLength + 3) & ~3; //BUGFIX: align to word size
|
||||||
|
cmd.infile = malloc(cmd.infileLength);
|
||||||
|
|
||||||
|
if (cmd.infile == NULL) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space while parsing the command line, exiting\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = strlen(argv[i]);
|
||||||
|
strncpy(cmd.infile, argv[i], len);
|
||||||
|
cmd.infile[len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!strcmp(argv[i], "--silent-print")) {
|
||||||
|
cmd.silentPrint = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!strcmp(argv[i], "--silent-assert")) {
|
||||||
|
cmd.silentAssert = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!strcmp(argv[i], "--remove-assert")) {
|
||||||
|
cmd.removeAssert = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--verbose")) {
|
||||||
|
cmd.verbose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
cmd.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debugging
|
||||||
|
static void debugStackPrint(Toy_Stack* stack) {
|
||||||
|
//DEBUG: if there's anything on the stack, print it
|
||||||
|
if (stack->count > 0) {
|
||||||
|
Toy_Bucket* stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
|
||||||
|
printf("\n" TOY_CC_NOTICE "Stack Dump" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s" TOY_CC_RESET "\n", "type", "value");
|
||||||
|
for (unsigned int i = 0; i < stack->count; i++) {
|
||||||
|
Toy_Value v = ((Toy_Value*)(stack + 1))[i]; //'stack + 1' is a naughty trick
|
||||||
|
|
||||||
|
//print type
|
||||||
|
printf("%-20s", Toy_getValueTypeAsCString(v.type));
|
||||||
|
|
||||||
|
//print value
|
||||||
|
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
|
||||||
|
char* buffer = Toy_getStringRaw(string);
|
||||||
|
printf("%-20s", buffer);
|
||||||
|
free(buffer);
|
||||||
|
Toy_freeString(string);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_freeBucket(&stringBucket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debugScopePrint(Toy_Scope* scope, int depth) {
|
||||||
|
//DEBUG: if there's anything in the scope, print it
|
||||||
|
if (scope->count > 0) {
|
||||||
|
Toy_Bucket* stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
|
||||||
|
printf("\n" TOY_CC_NOTICE "Scope Dump [%d]" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s%-20s" TOY_CC_RESET "\n", depth, "type", "name", "value");
|
||||||
|
for (unsigned int i = 0; i < scope->capacity; i++) {
|
||||||
|
if (scope->data[i].key == NULL || scope->data[i].key->info.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_String* k = scope->data[i].key;
|
||||||
|
Toy_Value v = scope->data[i].value;
|
||||||
|
|
||||||
|
printf("%-10s%-10s%-20s", Toy_getValueTypeAsCString(scope->data[i].type), scope->data[i].constant ? "const" : "", k != NULL ? k->leaf.data : "");
|
||||||
|
|
||||||
|
//print value
|
||||||
|
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
|
||||||
|
char* buffer = Toy_getStringRaw(string);
|
||||||
|
printf("%-20s", buffer);
|
||||||
|
free(buffer);
|
||||||
|
Toy_freeString(string);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_freeBucket(&stringBucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope->next != NULL) {
|
||||||
|
debugScopePrint(scope->next, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//repl function
|
||||||
|
int repl(const char* filepath, bool verbose) {
|
||||||
|
//output options
|
||||||
|
Toy_setPrintCallback(puts);
|
||||||
|
Toy_setErrorCallback(errorAndContinueCallback);
|
||||||
|
Toy_setAssertFailureCallback(assertFailureAndContinueCallback);
|
||||||
|
|
||||||
|
//vars to use
|
||||||
|
char prompt[256];
|
||||||
|
getFileName(prompt, filepath, 256);
|
||||||
|
unsigned int INPUT_BUFFER_SIZE = 4096;
|
||||||
|
char inputBuffer[INPUT_BUFFER_SIZE];
|
||||||
|
memset(inputBuffer, 0, INPUT_BUFFER_SIZE);
|
||||||
|
|
||||||
|
Toy_VM vm;
|
||||||
|
Toy_initVM(&vm);
|
||||||
|
|
||||||
|
printf("%s> ", prompt); //shows the terminal prompt and begin
|
||||||
|
|
||||||
|
//read from the terminal
|
||||||
|
while(fgets(inputBuffer, INPUT_BUFFER_SIZE, stdin)) {
|
||||||
|
//work around fgets() adding a newline
|
||||||
|
unsigned int length = strlen(inputBuffer);
|
||||||
|
if (inputBuffer[length - 1] == '\n') {
|
||||||
|
inputBuffer[--length] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == 0 || !inputBuffer[ strspn(inputBuffer, " \r\n\t") ]) {
|
||||||
|
printf("%s> ", prompt); //shows the terminal prompt and restart
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//end
|
||||||
|
if (strlen(inputBuffer) == 4 && (strncmp(inputBuffer, "exit", 4) == 0 || strncmp(inputBuffer, "quit", 4) == 0)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse the input, prep the VM for execution
|
||||||
|
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
Toy_Lexer lexer;
|
||||||
|
Toy_bindLexer(&lexer, inputBuffer);
|
||||||
|
Toy_Parser parser;
|
||||||
|
Toy_bindParser(&parser, &lexer);
|
||||||
|
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||||
|
|
||||||
|
//parsing error, retry
|
||||||
|
if (parser.error || ast == NULL) {
|
||||||
|
Toy_freeBucket(&bucket);
|
||||||
|
printf("%s> ", prompt); //shows the terminal prompt
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
inspect_ast(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||||
|
Toy_freeBucket(&bucket); //no need to for the GC here
|
||||||
|
|
||||||
|
if (bytecode == NULL) {
|
||||||
|
printf("%s> ", prompt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
inspect_bytecode(bytecode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//WARN: Hacky debugging
|
||||||
|
if (vm.scope == NULL) {
|
||||||
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
initStandardLibrary(&vm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//run
|
||||||
|
Toy_runVM(&vm);
|
||||||
|
|
||||||
|
int depthBeforeGC = 0;
|
||||||
|
int depthAfterGC = 0;
|
||||||
|
|
||||||
|
//print the debug info
|
||||||
|
if (verbose) {
|
||||||
|
debugStackPrint(vm.stack);
|
||||||
|
debugScopePrint(vm.scope, 0);
|
||||||
|
|
||||||
|
depthBeforeGC = inspect_bucket(&vm.memoryBucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
//free the memory, and leave the VM ready for the next loop
|
||||||
|
Toy_resetVM(&vm, true, true);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
depthAfterGC = inspect_bucket(&vm.memoryBucket);
|
||||||
|
|
||||||
|
printf("GC Report: %d -> %d\n", depthBeforeGC, depthAfterGC);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(bytecode);
|
||||||
|
|
||||||
|
printf("%s> ", prompt); //shows the terminal prompt
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup all memory
|
||||||
|
Toy_freeVM(&vm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//main file
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
Toy_setPrintCallback(puts);
|
||||||
|
Toy_setErrorCallback(errorAndExitCallback);
|
||||||
|
Toy_setAssertFailureCallback(assertFailureAndExitCallback);
|
||||||
|
|
||||||
|
//if there's args, process them
|
||||||
|
CmdLine cmd = parseCmdLine(argc, argv);
|
||||||
|
|
||||||
|
//output options
|
||||||
|
if (cmd.silentPrint) {
|
||||||
|
Toy_setPrintCallback(noOpCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.silentAssert) {
|
||||||
|
Toy_setAssertFailureCallback(silentExitCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
//process
|
||||||
|
if (cmd.error) {
|
||||||
|
usageCmdLine(argc, argv);
|
||||||
|
}
|
||||||
|
else if (cmd.help) {
|
||||||
|
helpCmdLine(argc, argv);
|
||||||
|
}
|
||||||
|
else if (cmd.version) {
|
||||||
|
versionCmdLine(argc, argv);
|
||||||
|
}
|
||||||
|
else if (cmd.infile != NULL) {
|
||||||
|
//read the source file
|
||||||
|
int size;
|
||||||
|
unsigned char* source = readFile(cmd.infile, &size);
|
||||||
|
|
||||||
|
//check the file
|
||||||
|
if (source == NULL) {
|
||||||
|
if (size == 0) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Could not parse an empty file '%s', exiting\n" TOY_CC_RESET, cmd.infile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (size == -1) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: File not found '%s', exiting\n" TOY_CC_RESET, cmd.infile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown error while reading file '%s', exiting\n" TOY_CC_RESET, cmd.infile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cmd.infile);
|
||||||
|
|
||||||
|
cmd.infile = NULL;
|
||||||
|
cmd.infileLength = 0;
|
||||||
|
|
||||||
|
//compile the source code
|
||||||
|
Toy_Lexer lexer;
|
||||||
|
Toy_bindLexer(&lexer, (char*)source);
|
||||||
|
|
||||||
|
Toy_Parser parser;
|
||||||
|
Toy_bindParser(&parser, &lexer);
|
||||||
|
|
||||||
|
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||||
|
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||||
|
|
||||||
|
if (ast == NULL) {
|
||||||
|
Toy_freeBucket(&bucket);
|
||||||
|
free(source);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.verbose) {
|
||||||
|
inspect_ast(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||||
|
Toy_freeBucket(&bucket);
|
||||||
|
free(source);
|
||||||
|
|
||||||
|
if (bytecode == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.verbose) {
|
||||||
|
inspect_bytecode(bytecode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//run the compiled code
|
||||||
|
Toy_VM vm;
|
||||||
|
Toy_initVM(&vm);
|
||||||
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
initStandardLibrary(&vm); //WARN: Hacky debugging
|
||||||
|
|
||||||
|
Toy_runVM(&vm);
|
||||||
|
|
||||||
|
//print the debug info
|
||||||
|
if (cmd.verbose) {
|
||||||
|
debugStackPrint(vm.stack);
|
||||||
|
debugScopePrint(vm.scope, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeVM(&vm);
|
||||||
|
free(bytecode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
repl(argv[0], cmd.verbose);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+54
-27
@@ -1,36 +1,63 @@
|
|||||||
|
#compiler settings
|
||||||
CC=gcc
|
CC=gcc
|
||||||
|
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
|
||||||
|
LIBS+=-lm -lToy
|
||||||
|
LDFLAGS+=-Wl,-rpath,'$$ORIGIN'
|
||||||
|
|
||||||
IDIR+=. ../source
|
ifeq ($(shell uname),Darwin) #make sure there's enough space for the dylib fix
|
||||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
LDFLAGS+=-Wl,-headerpad_max_install_names
|
||||||
LIBS+=-ltoy -lm
|
|
||||||
|
|
||||||
ODIR = obj
|
|
||||||
SRC = $(wildcard *.c)
|
|
||||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
|
||||||
OUTNAME=toy
|
|
||||||
OUT=../$(TOY_OUTDIR)/toyrepl
|
|
||||||
|
|
||||||
all: $(OBJ)
|
|
||||||
ifeq ($(shell uname),Darwin)
|
|
||||||
cp $(PWD)/$(TOY_OUTDIR)/lib$(OUTNAME).dylib /usr/local/lib/
|
|
||||||
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) $(LIBS)
|
|
||||||
else
|
|
||||||
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -Wl,-rpath,. -L$(realpath $(shell pwd)/../$(TOY_OUTDIR)) $(LIBS)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
release: all
|
|
||||||
strip $(OUT)
|
|
||||||
|
|
||||||
$(OBJ): | $(ODIR)
|
#directories
|
||||||
|
REPL_ROOTDIR=..
|
||||||
|
REPL_REPLDIR=.
|
||||||
|
REPL_SOURCEDIR=$(REPL_ROOTDIR)/$(TOY_SOURCEDIR)
|
||||||
|
|
||||||
$(ODIR):
|
REPL_OUTDIR=$(REPL_ROOTDIR)/$(TOY_OUTDIR)
|
||||||
mkdir $(ODIR)
|
REPL_OBJDIR=$(TOY_OBJDIR)
|
||||||
|
|
||||||
$(ODIR)/%.o: %.c
|
#file names
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
REPL_REPLFILES=$(wildcard $(REPL_REPLDIR)/*.c)
|
||||||
|
REPL_OBJFILES=$(addprefix $(REPL_OBJDIR)/,$(notdir $(REPL_REPLFILES:.c=.o)))
|
||||||
|
REPL_TARGETNAME=repl
|
||||||
|
|
||||||
.PHONY: clean
|
#file extensions
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
REPL_TARGETEXT=.exe
|
||||||
|
else
|
||||||
|
REPL_TARGETEXT=.out
|
||||||
|
endif
|
||||||
|
|
||||||
clean:
|
#linker fix
|
||||||
$(RM) $(ODIR)
|
LDFLAGS+=-L$(realpath $(REPL_OUTDIR))
|
||||||
rm /usr/local/lib/lib$(OUTNAME).dylib
|
|
||||||
|
#build the object files, compile the test cases, and run
|
||||||
|
all: build link
|
||||||
|
|
||||||
|
#targets for each step
|
||||||
|
.PHONY: build
|
||||||
|
build: $(REPL_OBJDIR) $(REPL_OBJFILES)
|
||||||
|
|
||||||
|
.PHONY: link
|
||||||
|
link: $(REPL_OUTDIR) $(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT)
|
||||||
|
|
||||||
|
#util targets
|
||||||
|
$(REPL_OUTDIR):
|
||||||
|
mkdir $(REPL_OUTDIR)
|
||||||
|
|
||||||
|
$(REPL_OBJDIR):
|
||||||
|
mkdir $(REPL_OBJDIR)
|
||||||
|
|
||||||
|
#compilation steps
|
||||||
|
$(REPL_OBJDIR)/%.o: $(REPL_REPLDIR)/%.c
|
||||||
|
$(CC) -c -o $@ $< $(addprefix -I,$(REPL_REPLDIR)) $(addprefix -I,$(REPL_SOURCEDIR)) $(CFLAGS)
|
||||||
|
|
||||||
|
$(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT): $(REPL_OBJFILES)
|
||||||
|
$(CC) -DTOY_IMPORT $(CFLAGS) -o $@ $(REPL_OBJFILES) $(LDFLAGS) $(LIBS)
|
||||||
|
ifeq ($(shell uname),Darwin) #dylib fix
|
||||||
|
otool -L $@
|
||||||
|
install_name_tool -add_rpath @executable_path/. $@
|
||||||
|
install_name_tool -change $(REPL_OUTDIR)/libToy.dylib @executable_path/libToy.dylib $@
|
||||||
|
otool -L $@
|
||||||
|
endif
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
#include "repl_tools.h"
|
|
||||||
#include "drive_system.h"
|
|
||||||
#include "lib_toy_version_info.h"
|
|
||||||
#include "lib_standard.h"
|
|
||||||
#include "lib_random.h"
|
|
||||||
#include "lib_runner.h"
|
|
||||||
#include "lib_math.h"
|
|
||||||
|
|
||||||
#include "toy_console_colors.h"
|
|
||||||
|
|
||||||
#include "toy.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define INPUT_BUFFER_SIZE 2048
|
|
||||||
|
|
||||||
void repl(const char* initialInput) {
|
|
||||||
//repl does it's own thing for now
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
char input[INPUT_BUFFER_SIZE];
|
|
||||||
memset(input, 0, INPUT_BUFFER_SIZE);
|
|
||||||
|
|
||||||
Toy_Interpreter interpreter; //persist the interpreter for the scopes
|
|
||||||
Toy_initInterpreter(&interpreter);
|
|
||||||
|
|
||||||
//inject the libs
|
|
||||||
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
|
|
||||||
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(;;) {
|
|
||||||
if (!initialInput) {
|
|
||||||
//handle EOF for exits
|
|
||||||
printf("> ");
|
|
||||||
if (!fgets(input, INPUT_BUFFER_SIZE, stdin)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//escape the repl (length of 5 to accomodate the newline)
|
|
||||||
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//setup this iteration
|
|
||||||
Toy_Lexer lexer;
|
|
||||||
Toy_Parser parser;
|
|
||||||
Toy_Compiler compiler;
|
|
||||||
|
|
||||||
Toy_initLexer(&lexer, initialInput ? initialInput : input);
|
|
||||||
Toy_private_setComments(&lexer, initialInput != NULL); //BUGFIX: disable comments here
|
|
||||||
Toy_initParser(&parser, &lexer);
|
|
||||||
Toy_initCompiler(&compiler);
|
|
||||||
|
|
||||||
//run this iteration
|
|
||||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
|
||||||
while(node != NULL) {
|
|
||||||
//pack up and restart
|
|
||||||
if (node->type == TOY_AST_NODE_ERROR) {
|
|
||||||
if (Toy_commandLine.verbose) {
|
|
||||||
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
|
|
||||||
}
|
|
||||||
error = true;
|
|
||||||
Toy_freeASTNode(node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_writeCompiler(&compiler, node);
|
|
||||||
Toy_freeASTNode(node);
|
|
||||||
node = Toy_scanParser(&parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
//get the bytecode dump
|
|
||||||
size_t size = 0;
|
|
||||||
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
|
||||||
|
|
||||||
//run the bytecode
|
|
||||||
Toy_runInterpreter(&interpreter, tb, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
//clean up this iteration
|
|
||||||
Toy_freeCompiler(&compiler);
|
|
||||||
Toy_freeParser(&parser);
|
|
||||||
error = false;
|
|
||||||
|
|
||||||
if (initialInput) {
|
|
||||||
free((void*)initialInput);
|
|
||||||
initialInput = NULL;
|
|
||||||
|
|
||||||
if (interpreter.panic) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_freeInterpreter(&interpreter);
|
|
||||||
}
|
|
||||||
|
|
||||||
//entry point
|
|
||||||
int main(int argc, const char* argv[]) {
|
|
||||||
Toy_initCommandLine(argc, argv);
|
|
||||||
|
|
||||||
//setup the drive system (for filesystem access)
|
|
||||||
Toy_initDriveSystem();
|
|
||||||
Toy_setDrivePath("scripts", "scripts");
|
|
||||||
|
|
||||||
//command line specific actions
|
|
||||||
if (Toy_commandLine.error) {
|
|
||||||
Toy_usageCommandLine(argc, argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Toy_commandLine.help) {
|
|
||||||
Toy_helpCommandLine(argc, argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Toy_commandLine.version) {
|
|
||||||
Toy_copyrightCommandLine(argc, argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//version
|
|
||||||
if (Toy_commandLine.verbose) {
|
|
||||||
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d, built '%s'\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
|
||||||
}
|
|
||||||
|
|
||||||
//run source file
|
|
||||||
if (Toy_commandLine.sourcefile) {
|
|
||||||
//only works on toy files
|
|
||||||
const char* s = strrchr(Toy_commandLine.sourcefile, '.');
|
|
||||||
if (!s || strcmp(s, ".toy")) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//run the source file
|
|
||||||
Toy_runSourceFile(Toy_commandLine.sourcefile);
|
|
||||||
|
|
||||||
//lib cleanup
|
|
||||||
Toy_freeDriveSystem();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//run from stdin
|
|
||||||
if (Toy_commandLine.source) {
|
|
||||||
Toy_runSource(Toy_commandLine.source);
|
|
||||||
|
|
||||||
//lib cleanup
|
|
||||||
Toy_freeDriveSystem();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//compile source file
|
|
||||||
if (Toy_commandLine.compilefile && Toy_commandLine.outfile) {
|
|
||||||
//only works on toy and tb files
|
|
||||||
const char* c = strrchr(Toy_commandLine.compilefile, '.');
|
|
||||||
if (!c || strcmp(c, ".toy")) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], c);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const char* o = strrchr(Toy_commandLine.outfile, '.');
|
|
||||||
if (!o || strcmp(o, ".tb")) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.tb', found '%s')" TOY_CC_RESET, argv[0], o);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//compile and save
|
|
||||||
size_t size = 0;
|
|
||||||
const char* source = (const char*)Toy_readFile(Toy_commandLine.compilefile, &size);
|
|
||||||
if (!source) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
const unsigned char* tb = Toy_compileString(source, &size);
|
|
||||||
if (!tb) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
Toy_writeFile(Toy_commandLine.outfile, tb, size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//run binary
|
|
||||||
if (Toy_commandLine.binaryfile) {
|
|
||||||
//only works on tb files
|
|
||||||
const char* c = strrchr(Toy_commandLine.binaryfile, '.');
|
|
||||||
if (!c || strcmp(c, ".tb")) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.tb', found '%s')" TOY_CC_RESET, argv[0], c); //this one is never seen
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Toy_commandLine.parseBytecodeHeader) {
|
|
||||||
//only parse the bytecode header
|
|
||||||
Toy_parseBinaryFileHeader(Toy_commandLine.binaryfile);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//run the binary file
|
|
||||||
Toy_runBinaryFile(Toy_commandLine.binaryfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
//lib cleanup
|
|
||||||
Toy_freeDriveSystem();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* initialSource = NULL;
|
|
||||||
if (Toy_commandLine.initialfile) {
|
|
||||||
//only works on toy files
|
|
||||||
const char* s = strrchr(Toy_commandLine.initialfile, '.');
|
|
||||||
if (!s || strcmp(s, ".toy")) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size;
|
|
||||||
initialSource = (const char*)Toy_readFile(Toy_commandLine.initialfile, &size);
|
|
||||||
}
|
|
||||||
|
|
||||||
repl(initialSource);
|
|
||||||
|
|
||||||
//lib cleanup
|
|
||||||
Toy_freeDriveSystem();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
#include "repl_tools.h"
|
|
||||||
#include "lib_toy_version_info.h"
|
|
||||||
#include "lib_standard.h"
|
|
||||||
#include "lib_random.h"
|
|
||||||
#include "lib_runner.h"
|
|
||||||
#include "lib_math.h"
|
|
||||||
|
|
||||||
#include "toy_console_colors.h"
|
|
||||||
|
|
||||||
#include "toy_lexer.h"
|
|
||||||
#include "toy_parser.h"
|
|
||||||
#include "toy_compiler.h"
|
|
||||||
#include "toy_interpreter.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
//IO functions
|
|
||||||
const unsigned char* Toy_readFile(const char* path, size_t* fileSize) {
|
|
||||||
FILE* file = fopen(path, "rb");
|
|
||||||
|
|
||||||
if (file == NULL) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(file, 0L, SEEK_END);
|
|
||||||
*fileSize = ftell(file);
|
|
||||||
rewind(file);
|
|
||||||
|
|
||||||
unsigned char* buffer = (unsigned char*)malloc(*fileSize + 1);
|
|
||||||
|
|
||||||
if (buffer == NULL) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bytesRead = fread(buffer, sizeof(unsigned char), *fileSize, file);
|
|
||||||
|
|
||||||
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
|
||||||
|
|
||||||
if (bytesRead < *fileSize) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
|
|
||||||
FILE* file = fopen(path, "wb");
|
|
||||||
|
|
||||||
if (file == NULL) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t written = fwrite(bytes, size, 1, file);
|
|
||||||
|
|
||||||
if (written != 1) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//repl functions
|
|
||||||
const unsigned char* Toy_compileString(const char* source, size_t* size) {
|
|
||||||
Toy_Lexer lexer;
|
|
||||||
Toy_Parser parser;
|
|
||||||
Toy_Compiler compiler;
|
|
||||||
|
|
||||||
Toy_initLexer(&lexer, source);
|
|
||||||
Toy_initParser(&parser, &lexer);
|
|
||||||
Toy_initCompiler(&compiler);
|
|
||||||
|
|
||||||
//step 1 - run the parser until the end of the source
|
|
||||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
|
||||||
while(node != NULL) {
|
|
||||||
//on error, pack up and leave
|
|
||||||
if (node->type == TOY_AST_NODE_ERROR) {
|
|
||||||
Toy_freeASTNode(node);
|
|
||||||
Toy_freeCompiler(&compiler);
|
|
||||||
Toy_freeParser(&parser);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_writeCompiler(&compiler, node);
|
|
||||||
Toy_freeASTNode(node);
|
|
||||||
node = Toy_scanParser(&parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
//step 2 - get the bytecode dump
|
|
||||||
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
|
|
||||||
|
|
||||||
//cleanup
|
|
||||||
Toy_freeCompiler(&compiler);
|
|
||||||
Toy_freeParser(&parser);
|
|
||||||
//no lexer to clean up
|
|
||||||
|
|
||||||
//finally
|
|
||||||
return tb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_runBinary(const unsigned char* tb, size_t size) {
|
|
||||||
Toy_Interpreter interpreter;
|
|
||||||
Toy_initInterpreter(&interpreter);
|
|
||||||
|
|
||||||
//inject the libs
|
|
||||||
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
|
|
||||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
|
||||||
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
|
|
||||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
|
||||||
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
|
|
||||||
|
|
||||||
Toy_runInterpreter(&interpreter, tb, (int)size);
|
|
||||||
Toy_freeInterpreter(&interpreter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_runBinaryFile(const char* fname) {
|
|
||||||
size_t size = 0; //not used
|
|
||||||
const unsigned char* tb = Toy_readFile(fname, &size);
|
|
||||||
if (!tb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Toy_runBinary(tb, size);
|
|
||||||
//interpreter takes ownership of the binary data
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_runSource(const char* source) {
|
|
||||||
size_t size = 0;
|
|
||||||
const unsigned char* tb = Toy_compileString(source, &size);
|
|
||||||
if (!tb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_runBinary(tb, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_runSourceFile(const char* fname) {
|
|
||||||
size_t size = 0; //not used
|
|
||||||
const char* source = (const char*)Toy_readFile(fname, &size);
|
|
||||||
if (!source) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Toy_runSource(source);
|
|
||||||
free((void*)source);
|
|
||||||
}
|
|
||||||
|
|
||||||
//utils for debugging the header
|
|
||||||
static unsigned char readByte(const unsigned char* tb, int* count) {
|
|
||||||
unsigned char ret = *(unsigned char*)(tb + *count);
|
|
||||||
*count += 1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* readString(const unsigned char* tb, int* count) {
|
|
||||||
const unsigned char* ret = tb + *count;
|
|
||||||
*count += (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);
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#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"
|
|
||||||
|
|
||||||
/*!
|
|
||||||
## Defined Functions
|
|
||||||
!*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
### const char* Toy_readFile(const char* path, size_t* fileSize)
|
|
||||||
|
|
||||||
This function reads in a file, and returns it as a constant buffer. It also sets the variable pointed to by `fileSize` to the size of the given buffer.
|
|
||||||
|
|
||||||
On error, this function returns `NULL`.
|
|
||||||
!*/
|
|
||||||
const unsigned char* Toy_readFile(const char* path, size_t* fileSize);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
### int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size)
|
|
||||||
|
|
||||||
This function writes the buffer pointed to by `bytes` to a file specified by `path`. The buffer's size should be specified by `size`.
|
|
||||||
|
|
||||||
On error, this function returns a non-zero value.
|
|
||||||
!*/
|
|
||||||
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
### 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);
|
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#include "standard_library.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include "toy_print.h"
|
||||||
|
#include "toy_scope.h"
|
||||||
|
#include "toy_stack.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct CallbackPairs {
|
||||||
|
const char* name;
|
||||||
|
Toy_nativeCallback callback;
|
||||||
|
} CallbackPairs;
|
||||||
|
|
||||||
|
//example callbacks
|
||||||
|
static void answer(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)vm;
|
||||||
|
(void)self;
|
||||||
|
Toy_print(TOY_CC_DEBUG "This function returns the integer '42' to the calling scope." TOY_CC_RESET);
|
||||||
|
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void identity(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
//does nothing, but any arguements are left on the stack as results
|
||||||
|
(void)vm;
|
||||||
|
(void)self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void echo(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
//pops one argument, and prints it
|
||||||
|
Toy_Value value = Toy_popStack(&vm->stack);
|
||||||
|
Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value);
|
||||||
|
char* cstr = Toy_getStringRaw(string);
|
||||||
|
|
||||||
|
Toy_print(cstr);
|
||||||
|
|
||||||
|
free(cstr);
|
||||||
|
Toy_freeString(string);
|
||||||
|
Toy_freeValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void next(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
//used by 'range'
|
||||||
|
if (self->meta2 < self->meta1) {
|
||||||
|
Toy_Value result = TOY_VALUE_FROM_INTEGER(self->meta2);
|
||||||
|
Toy_pushStack(&vm->stack, result);
|
||||||
|
self->meta2++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void range(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
//one arg to represent the number of iterations
|
||||||
|
Toy_Value value = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
//check types
|
||||||
|
if (!TOY_VALUE_IS_INTEGER(value)) {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Expected Integer argument in 'range', found '%s'", Toy_getValueTypeAsCString(value.type));
|
||||||
|
Toy_error(buffer);
|
||||||
|
Toy_freeValue(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//make the callback
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, next);
|
||||||
|
fn->native.meta1 = TOY_VALUE_AS_INTEGER(value); //fake a closure
|
||||||
|
fn->native.meta2 = 0; //counter
|
||||||
|
|
||||||
|
Toy_Value result = TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
|
||||||
|
Toy_pushStack(&vm->stack, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CallbackPairs callbackPairs[] = {
|
||||||
|
{"dbg_answer", answer},
|
||||||
|
{"dbg_identity", identity},
|
||||||
|
{"dbg_echo", echo},
|
||||||
|
{"range", range},
|
||||||
|
|
||||||
|
{NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
void initStandardLibrary(Toy_VM* vm) {
|
||||||
|
if (vm == NULL || vm->scope == NULL || vm->memoryBucket == NULL) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't initialize standard library, exiting\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//declare each pair
|
||||||
|
for (int i = 0; callbackPairs[i].name; i++) {
|
||||||
|
Toy_String* key = Toy_createStringLength(&vm->memoryBucket, callbackPairs[i].name, strlen(callbackPairs[i].name));
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&(vm->memoryBucket), callbackPairs[i].callback);
|
||||||
|
|
||||||
|
Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_vm.h"
|
||||||
|
|
||||||
|
void initStandardLibrary(Toy_VM*);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//calculate the nth fibonacci number, and print it
|
||||||
|
|
||||||
|
var counter: Int = 0;
|
||||||
|
|
||||||
|
var first: Int = 1;
|
||||||
|
var second: Int = 0;
|
||||||
|
|
||||||
|
while (counter < 100_000) {
|
||||||
|
var third: Int = first + second;
|
||||||
|
first = second;
|
||||||
|
second = third;
|
||||||
|
|
||||||
|
print third;
|
||||||
|
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
fn output(arg) {
|
||||||
|
print arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
var array = ["alpha", "bravo", "charlie"];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
array.forEach(echo);
|
||||||
|
array.forEach(output);
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
//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;
|
|
||||||
}
|
|
||||||
+9
-6
@@ -1,10 +1,13 @@
|
|||||||
//WARNING: please think twice before using this in a test
|
//tentatively functional
|
||||||
fn fib(n : int) {
|
|
||||||
|
//fibonacci sequence
|
||||||
|
fn fib(n) {
|
||||||
if (n < 2) return n;
|
if (n < 2) return n;
|
||||||
return fib(n-1) + fib(n-2);
|
return fib(n-1) + fib(n-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i <= 35; i++) {
|
print fib(12);
|
||||||
var res = fib(i);
|
|
||||||
print string i + ": " + string res;
|
//Note to my future self: yes, the base case in 'fib()' is 'n < 2', stop second guessing yourself!
|
||||||
}
|
//Note to my past self: don't tell me what to do!
|
||||||
|
//Note to both of you: keep it down you young whipper snappers!
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//standard example, using 'while' instead of 'for', because it's not ready yet
|
||||||
|
|
||||||
|
var counter: Int = 0;
|
||||||
|
|
||||||
|
while (++counter <= 100) {
|
||||||
|
var result: String = "";
|
||||||
|
|
||||||
|
if (counter % 3 == 0) {
|
||||||
|
result = result .. "fizz";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter % 5 == 0) {
|
||||||
|
result = result .. "buzz";
|
||||||
|
}
|
||||||
|
|
||||||
|
//finally
|
||||||
|
if (result != "") {
|
||||||
|
print result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//WARN: This is just a scratch pad, don't use it
|
||||||
|
//TODO: table.hasValue or table.getKeyFromValue?
|
||||||
|
|
||||||
|
|
||||||
|
//for (var i in array) print i;
|
||||||
|
//for (var i in table) print i;
|
||||||
|
//for (var i in range(10)) print i;
|
||||||
|
//for (range(10)) print "ha";
|
||||||
|
|
||||||
|
|
||||||
|
//example of a `range`-like function
|
||||||
|
fn range(limit: Int) {
|
||||||
|
var counter: Int = 0;
|
||||||
|
|
||||||
|
fn next() {
|
||||||
|
if (counter >= limit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else return counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
var next = range(10);
|
||||||
|
|
||||||
|
|
||||||
|
fn log(x) {
|
||||||
|
if (x == null) return;
|
||||||
|
print x;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
log(next());
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn a(x) {
|
||||||
|
print x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
a(b(), b());
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//find the leap years
|
||||||
|
fn isLeapYear(n: Int) {
|
||||||
|
if (n % 400 == 0) return true;
|
||||||
|
if (n % 100 == 0) return false;
|
||||||
|
return n % 4 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for string reuse
|
||||||
|
{
|
||||||
|
print isLeapYear(1999);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print isLeapYear(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print isLeapYear(2004);
|
||||||
|
}
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var randi: Int = 69420;
|
||||||
|
fn rand() {
|
||||||
|
return randi = randi * 1664525 + 1013904223;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var a = rand();
|
||||||
|
|
||||||
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
//number of iterations
|
|
||||||
var SIZE: int const = 100;
|
|
||||||
|
|
||||||
//lookup table
|
|
||||||
var lookup = [
|
|
||||||
"*": [
|
|
||||||
"*": [
|
|
||||||
"*": " ",
|
|
||||||
" ": "*"
|
|
||||||
],
|
|
||||||
" ": [
|
|
||||||
"*": "*",
|
|
||||||
" ": " "
|
|
||||||
]
|
|
||||||
], " ": [
|
|
||||||
"*": [
|
|
||||||
"*": "*",
|
|
||||||
" ": "*"
|
|
||||||
],
|
|
||||||
" ": [
|
|
||||||
"*": "*",
|
|
||||||
" ": " "
|
|
||||||
]
|
|
||||||
]];
|
|
||||||
|
|
||||||
//initial line to build from
|
|
||||||
var prev: string = "";
|
|
||||||
for (var i = 0; i < SIZE -1; i++) {
|
|
||||||
prev += " ";
|
|
||||||
}
|
|
||||||
prev += "*"; //initial
|
|
||||||
print prev;
|
|
||||||
|
|
||||||
//run
|
|
||||||
for (var iteration = 0; iteration < SIZE -1; iteration++) {
|
|
||||||
//left
|
|
||||||
var output = (lookup[" "][prev[0]][prev[1]]);
|
|
||||||
|
|
||||||
//middle
|
|
||||||
for (var i = 1; i < SIZE-1; i++) {
|
|
||||||
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//right
|
|
||||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
|
||||||
|
|
||||||
print output;
|
|
||||||
prev = output;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
fn makeCounter() {
|
||||||
|
var counter: Int = 0;
|
||||||
|
|
||||||
|
fn increment() {
|
||||||
|
return ++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tally = makeCounter();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var result = tally();
|
||||||
|
|
||||||
|
print result;
|
||||||
|
|
||||||
|
if (result >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
fn f() {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
fn g() {
|
|
||||||
fn i() {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn h() {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
+41
-36
@@ -1,54 +1,59 @@
|
|||||||
|
#compiler settings
|
||||||
CC=gcc
|
CC=gcc
|
||||||
|
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
|
||||||
|
LIBS+=-lm
|
||||||
|
LDFLAGS+=
|
||||||
|
|
||||||
IDIR+=.
|
#directories
|
||||||
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
SRC_ROOTDIR=..
|
||||||
LIBS+=
|
SRC_SOURCEDIR=.
|
||||||
|
|
||||||
ODIR = obj
|
SRC_OUTDIR=$(SRC_ROOTDIR)/$(TOY_OUTDIR)
|
||||||
SRC = $(wildcard *.c)
|
SRC_OBJDIR=$(TOY_OBJDIR)
|
||||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
|
||||||
|
|
||||||
OUTNAME=toy
|
#file names
|
||||||
|
SRC_SOURCEFILES=$(wildcard $(SRC_SOURCEDIR)/*.c)
|
||||||
|
SRC_OBJFILES=$(addprefix $(SRC_OBJDIR)/,$(notdir $(SRC_SOURCEFILES:.c=.o)))
|
||||||
|
SRC_TARGETNAME=Toy
|
||||||
|
|
||||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
#SRC_LIBLINE is a fancy way of making the linker work correctly
|
||||||
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
|
ifeq ($(shell uname),Linux)
|
||||||
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
SRC_TARGETEXT=.so
|
||||||
else ifeq ($(shell uname),Linux)
|
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive
|
||||||
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
CFLAGS+=-fPIC
|
||||||
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
|
else ifeq ($(shell uname),NetBSD)
|
||||||
|
SRC_TARGETEXT=.so
|
||||||
|
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive
|
||||||
CFLAGS+=-fPIC
|
CFLAGS+=-fPIC
|
||||||
else ifeq ($(OS),Windows_NT)
|
else ifeq ($(OS),Windows_NT)
|
||||||
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
|
SRC_TARGETEXT=.dll
|
||||||
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive -Wl,--export-all-symbols -Wl,--enable-auto-import
|
||||||
else ifeq ($(shell uname),Darwin)
|
else ifeq ($(shell uname),Darwin)
|
||||||
LIBLINE = $(OBJ)
|
SRC_TARGETEXT=.dylib
|
||||||
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).dylib
|
SRC_LIBLINE=-shared -Wl,-rpath,. $(SRC_OBJFILES)
|
||||||
else
|
else
|
||||||
@echo "Platform test failed - what platform is this?"
|
@echo "Platform test failed - what platform is this?"
|
||||||
exit 1
|
exit 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
library: $(OBJ)
|
#build the object files, compile the test cases, and run
|
||||||
$(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE)
|
all: build link
|
||||||
|
|
||||||
static: $(OBJ)
|
#targets for each step
|
||||||
ar crs ../$(TOY_OUTDIR)/lib$(OUTNAME).a $(OBJ)
|
.PHONY: build
|
||||||
|
build: $(SRC_OUTDIR) $(SRC_OBJDIR) $(SRC_OBJFILES)
|
||||||
|
|
||||||
library-release: $(OBJ) library
|
.PHONY: link
|
||||||
strip $(OUT)
|
link: $(SRC_OUTDIR)
|
||||||
|
$(CC) -DTOY_EXPORT $(CFLAGS) -o $(SRC_OUTDIR)/lib$(SRC_TARGETNAME)$(SRC_TARGETEXT) $(SRC_LIBLINE)
|
||||||
|
|
||||||
static-release: $(OBJ) static
|
#util targets
|
||||||
strip -d ../$(TOY_OUTDIR)/lib$(OUTNAME).a
|
$(SRC_OUTDIR):
|
||||||
|
mkdir $(SRC_OUTDIR)
|
||||||
|
|
||||||
$(OBJ): | $(ODIR)
|
$(SRC_OBJDIR):
|
||||||
|
mkdir $(SRC_OBJDIR)
|
||||||
|
|
||||||
$(ODIR):
|
#compilation steps
|
||||||
mkdir $(ODIR)
|
$(SRC_OBJDIR)/%.o: $(SRC_SOURCEDIR)/%.c
|
||||||
|
$(CC) -c -o $@ $< $(addprefix -I,$(SRC_SOURCEDIR)) $(CFLAGS)
|
||||||
$(ODIR)/%.o: %.c
|
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) $(ODIR)
|
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
#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,35 @@
|
|||||||
|
#include "toy_array.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
|
||||||
|
//if some values will be removed, free them first
|
||||||
|
if (paramArray != NULL && paramArray->count > capacity) {
|
||||||
|
for (unsigned int i = capacity; i < paramArray->count; i++) {
|
||||||
|
Toy_freeValue(paramArray->data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if you're freeing everything, just return
|
||||||
|
if (capacity == 0) {
|
||||||
|
free(paramArray);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity;
|
||||||
|
|
||||||
|
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
|
||||||
|
|
||||||
|
if (array == NULL) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize a 'Toy_Array' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
array->capacity = capacity;
|
||||||
|
array->count = paramArray == NULL ? 0 :
|
||||||
|
(array->count > capacity ? capacity : array->count); //truncate lost data
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_value.h"
|
||||||
|
|
||||||
|
//standard generic array
|
||||||
|
typedef struct Toy_Array { //32 | 64 BITNESS
|
||||||
|
unsigned int capacity; //4 | 4
|
||||||
|
unsigned int count; //4 | 4
|
||||||
|
Toy_Value data[]; //- | -
|
||||||
|
} Toy_Array; //8 | 8
|
||||||
|
|
||||||
|
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
|
||||||
|
|
||||||
|
//some useful sizes, could be swapped out as needed
|
||||||
|
#ifndef TOY_ARRAY_INITIAL_CAPACITY
|
||||||
|
#define TOY_ARRAY_INITIAL_CAPACITY 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_ARRAY_EXPANSION_RATE
|
||||||
|
#define TOY_ARRAY_EXPANSION_RATE 2
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
#include "toy_ast.h"
|
||||||
|
|
||||||
|
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_BLOCK;
|
||||||
|
tmp->block.innerScope = false;
|
||||||
|
tmp->block.child = NULL;
|
||||||
|
tmp->block.next = NULL;
|
||||||
|
tmp->block.tail = NULL;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child) {
|
||||||
|
//first, check if we're an empty head
|
||||||
|
if (block->block.child == NULL) {
|
||||||
|
block->block.child = child;
|
||||||
|
return; //First call on an empty head skips any memory allocations
|
||||||
|
}
|
||||||
|
|
||||||
|
//run (or jump) until we hit the current tail
|
||||||
|
Toy_Ast* iter = block->block.tail ? block->block.tail : block;
|
||||||
|
|
||||||
|
while(iter->block.next != NULL) {
|
||||||
|
iter = iter->block.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
//append a new link to the chain
|
||||||
|
Toy_private_initAstBlock(bucketHandle, &(iter->block.next));
|
||||||
|
|
||||||
|
//store the child in the new link, prep the tail pointer
|
||||||
|
iter->block.next->block.child = child;
|
||||||
|
block->block.tail = iter->block.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_VALUE;
|
||||||
|
tmp->value.value = value;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_UNARY;
|
||||||
|
tmp->unary.flag = flag;
|
||||||
|
tmp->unary.child = *astHandle;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_BINARY;
|
||||||
|
tmp->binary.flag = flag;
|
||||||
|
tmp->binary.left = *astHandle; //left-recursive
|
||||||
|
tmp->binary.right = right;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_BINARY_SHORT_CIRCUIT;
|
||||||
|
tmp->binary.flag = flag;
|
||||||
|
tmp->binary.left = *astHandle; //left-recursive
|
||||||
|
tmp->binary.right = right;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_COMPARE;
|
||||||
|
tmp->compare.flag = flag;
|
||||||
|
tmp->compare.left = *astHandle; //left-recursive
|
||||||
|
tmp->compare.right = right;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_GROUP;
|
||||||
|
tmp->group.child = (*astHandle);
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_COMPOUND;
|
||||||
|
tmp->compound.flag = flag;
|
||||||
|
tmp->compound.child = *astHandle;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_AGGREGATE;
|
||||||
|
tmp->aggregate.flag = flag;
|
||||||
|
tmp->aggregate.left = *astHandle; //left-recursive
|
||||||
|
tmp->aggregate.right = right;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_ASSERT;
|
||||||
|
tmp->assert.child = child;
|
||||||
|
tmp->assert.message = msg;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_IF_THEN_ELSE;
|
||||||
|
tmp->ifThenElse.condBranch = condBranch;
|
||||||
|
tmp->ifThenElse.thenBranch = thenBranch;
|
||||||
|
tmp->ifThenElse.elseBranch = elseBranch;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_WHILE_THEN;
|
||||||
|
tmp->whileThen.condBranch = condBranch;
|
||||||
|
tmp->whileThen.thenBranch = thenBranch;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_FOR_THEN;
|
||||||
|
tmp->forThen.condBranch = condBranch;
|
||||||
|
tmp->forThen.thenBranch = thenBranch;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_BREAK;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_CONTINUE;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_RETURN;
|
||||||
|
tmp->fnReturn.child = (*astHandle);
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_PRINT;
|
||||||
|
tmp->print.child = (*astHandle);
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_VAR_DECLARE;
|
||||||
|
tmp->varDeclare.name = name;
|
||||||
|
tmp->varDeclare.valueType = valueType;
|
||||||
|
tmp->varDeclare.constant = constant;
|
||||||
|
tmp->varDeclare.expr = expr;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_VAR_ASSIGN;
|
||||||
|
tmp->varAssign.flag = flag;
|
||||||
|
tmp->varAssign.target = (*astHandle);
|
||||||
|
tmp->varAssign.expr = expr;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_VAR_ACCESS;
|
||||||
|
tmp->varAccess.child = (*astHandle);
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_FN_DECLARE;
|
||||||
|
tmp->fnDeclare.name = name;
|
||||||
|
tmp->fnDeclare.params = params;
|
||||||
|
tmp->fnDeclare.body = body;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* args) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_FN_INVOKE;
|
||||||
|
tmp->fnInvoke.function = (*astHandle);
|
||||||
|
tmp->fnInvoke.args = args;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_ATTRIBUTE;
|
||||||
|
tmp->attribute.left = (*astHandle);
|
||||||
|
tmp->attribute.right = expr;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_ITERABLE;
|
||||||
|
tmp->iterable.left = (*astHandle);
|
||||||
|
tmp->iterable.right = expr;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_STACK_POP;
|
||||||
|
tmp->stackPop.child = (*astHandle);
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_PASS;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_ERROR;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||||
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
|
tmp->type = TOY_AST_END;
|
||||||
|
|
||||||
|
(*astHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
|
||||||
|
switch(type) {
|
||||||
|
case TOY_AST_BLOCK: return "BLOCK";
|
||||||
|
|
||||||
|
case TOY_AST_VALUE: return "VALUE";
|
||||||
|
case TOY_AST_UNARY: return "UNARY";
|
||||||
|
case TOY_AST_BINARY: return "BINARY";
|
||||||
|
case TOY_AST_BINARY_SHORT_CIRCUIT: return "BINARY_SHORT_CIRCUIT";
|
||||||
|
case TOY_AST_COMPARE: return "COMPARE";
|
||||||
|
case TOY_AST_GROUP: return "GROUP";
|
||||||
|
case TOY_AST_COMPOUND: return "COMPOUND";
|
||||||
|
case TOY_AST_AGGREGATE: return "AGGREGATE";
|
||||||
|
|
||||||
|
case TOY_AST_ASSERT: return "ASSERT";
|
||||||
|
case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE";
|
||||||
|
case TOY_AST_WHILE_THEN: return "WHILE_THEN";
|
||||||
|
case TOY_AST_FOR_THEN: return "FOR_THEN";
|
||||||
|
case TOY_AST_BREAK: return "BREAK";
|
||||||
|
case TOY_AST_CONTINUE: return "CONTINUE";
|
||||||
|
case TOY_AST_RETURN: return "RETURN";
|
||||||
|
case TOY_AST_PRINT: return "PRINT";
|
||||||
|
|
||||||
|
case TOY_AST_VAR_DECLARE: return "DECLARE";
|
||||||
|
case TOY_AST_VAR_ASSIGN: return "ASSIGN";
|
||||||
|
case TOY_AST_VAR_ACCESS: return "ACCESS";
|
||||||
|
|
||||||
|
case TOY_AST_FN_DECLARE: return "FN_DECLARE";
|
||||||
|
case TOY_AST_FN_INVOKE: return "FN_INVOKE";
|
||||||
|
case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
|
||||||
|
case TOY_AST_ITERABLE: return "ITERABLE";
|
||||||
|
|
||||||
|
case TOY_AST_STACK_POP: return "STACK_POP";
|
||||||
|
|
||||||
|
case TOY_AST_PASS: return "PASS";
|
||||||
|
case TOY_AST_ERROR: return "ERROR";
|
||||||
|
case TOY_AST_END: return "END";
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_bucket.h"
|
||||||
|
#include "toy_value.h"
|
||||||
|
#include "toy_string.h"
|
||||||
|
|
||||||
|
//each major type
|
||||||
|
typedef enum Toy_AstType {
|
||||||
|
TOY_AST_BLOCK,
|
||||||
|
|
||||||
|
TOY_AST_VALUE,
|
||||||
|
TOY_AST_UNARY,
|
||||||
|
TOY_AST_BINARY,
|
||||||
|
TOY_AST_BINARY_SHORT_CIRCUIT,
|
||||||
|
TOY_AST_COMPARE,
|
||||||
|
TOY_AST_GROUP,
|
||||||
|
TOY_AST_COMPOUND,
|
||||||
|
TOY_AST_AGGREGATE,
|
||||||
|
|
||||||
|
TOY_AST_ASSERT,
|
||||||
|
TOY_AST_IF_THEN_ELSE,
|
||||||
|
TOY_AST_WHILE_THEN,
|
||||||
|
TOY_AST_FOR_THEN,
|
||||||
|
TOY_AST_BREAK,
|
||||||
|
TOY_AST_CONTINUE,
|
||||||
|
TOY_AST_RETURN,
|
||||||
|
TOY_AST_PRINT,
|
||||||
|
|
||||||
|
TOY_AST_VAR_DECLARE,
|
||||||
|
TOY_AST_VAR_ASSIGN,
|
||||||
|
TOY_AST_VAR_ACCESS,
|
||||||
|
|
||||||
|
TOY_AST_FN_DECLARE,
|
||||||
|
TOY_AST_FN_INVOKE,
|
||||||
|
TOY_AST_ATTRIBUTE,
|
||||||
|
TOY_AST_ITERABLE,
|
||||||
|
|
||||||
|
TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements
|
||||||
|
|
||||||
|
TOY_AST_PASS,
|
||||||
|
TOY_AST_ERROR,
|
||||||
|
TOY_AST_END,
|
||||||
|
} Toy_AstType;
|
||||||
|
|
||||||
|
//flags are handled differently by different types
|
||||||
|
typedef enum Toy_AstFlag {
|
||||||
|
TOY_AST_FLAG_NONE = 0,
|
||||||
|
|
||||||
|
//binary flags
|
||||||
|
TOY_AST_FLAG_ADD = 1,
|
||||||
|
TOY_AST_FLAG_SUBTRACT = 2,
|
||||||
|
TOY_AST_FLAG_MULTIPLY = 3,
|
||||||
|
TOY_AST_FLAG_DIVIDE = 4,
|
||||||
|
TOY_AST_FLAG_MODULO = 5,
|
||||||
|
|
||||||
|
TOY_AST_FLAG_AND = 6,
|
||||||
|
TOY_AST_FLAG_OR = 7,
|
||||||
|
TOY_AST_FLAG_CONCAT = 8,
|
||||||
|
|
||||||
|
TOY_AST_FLAG_ASSIGN = 10,
|
||||||
|
TOY_AST_FLAG_ADD_ASSIGN = 11,
|
||||||
|
TOY_AST_FLAG_SUBTRACT_ASSIGN = 12,
|
||||||
|
TOY_AST_FLAG_MULTIPLY_ASSIGN = 13,
|
||||||
|
TOY_AST_FLAG_DIVIDE_ASSIGN = 14,
|
||||||
|
TOY_AST_FLAG_MODULO_ASSIGN = 15,
|
||||||
|
|
||||||
|
TOY_AST_FLAG_COMPARE_EQUAL = 20,
|
||||||
|
TOY_AST_FLAG_COMPARE_NOT = 21,
|
||||||
|
TOY_AST_FLAG_COMPARE_LESS = 22,
|
||||||
|
TOY_AST_FLAG_COMPARE_LESS_EQUAL = 23,
|
||||||
|
TOY_AST_FLAG_COMPARE_GREATER = 24,
|
||||||
|
TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25,
|
||||||
|
|
||||||
|
TOY_AST_FLAG_COMPOUND_ARRAY = 30,
|
||||||
|
TOY_AST_FLAG_COMPOUND_TABLE = 31,
|
||||||
|
TOY_AST_FLAG_COLLECTION = 32,
|
||||||
|
TOY_AST_FLAG_PAIR = 33,
|
||||||
|
TOY_AST_FLAG_INDEX = 34,
|
||||||
|
TOY_AST_FLAG_FN_ARGUMENTS = 35,
|
||||||
|
|
||||||
|
//unary flags
|
||||||
|
TOY_AST_FLAG_NEGATE = 40,
|
||||||
|
TOY_AST_FLAG_PREFIX_INCREMENT = 41,
|
||||||
|
TOY_AST_FLAG_PREFIX_DECREMENT = 42,
|
||||||
|
TOY_AST_FLAG_POSTFIX_INCREMENT = 43,
|
||||||
|
TOY_AST_FLAG_POSTFIX_DECREMENT = 44,
|
||||||
|
|
||||||
|
TOY_AST_FLAG_INVOKATION = 45,
|
||||||
|
TOY_AST_FLAG_ATTRIBUTE = 46,
|
||||||
|
|
||||||
|
// TOY_AST_FLAG_TERNARY,
|
||||||
|
} Toy_AstFlag;
|
||||||
|
|
||||||
|
//the root AST type
|
||||||
|
typedef union Toy_Ast Toy_Ast;
|
||||||
|
|
||||||
|
typedef struct Toy_AstBlock {
|
||||||
|
Toy_AstType type;
|
||||||
|
bool innerScope;
|
||||||
|
Toy_Ast* child; //begin encoding the line
|
||||||
|
Toy_Ast* next; //'next' is either an AstBlock or null
|
||||||
|
Toy_Ast* tail; //'tail' - either points to the tail of the current list, or null; only used as an optimisation in toy_ast.c
|
||||||
|
} Toy_AstBlock;
|
||||||
|
|
||||||
|
typedef struct Toy_AstValue {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Value value;
|
||||||
|
} Toy_AstValue;
|
||||||
|
|
||||||
|
typedef struct Toy_AstUnary {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstUnary;
|
||||||
|
|
||||||
|
typedef struct Toy_AstBinary {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* left;
|
||||||
|
Toy_Ast* right;
|
||||||
|
} Toy_AstBinary;
|
||||||
|
|
||||||
|
typedef struct Toy_AstBinaryShortCircuit {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* left;
|
||||||
|
Toy_Ast* right;
|
||||||
|
} Toy_AstBinaryShortCircuit;
|
||||||
|
|
||||||
|
typedef struct Toy_AstCompare {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* left;
|
||||||
|
Toy_Ast* right;
|
||||||
|
} Toy_AstCompare;
|
||||||
|
|
||||||
|
typedef struct Toy_AstGroup {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstGroup;
|
||||||
|
|
||||||
|
typedef struct Toy_AstCompound {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstCompound;
|
||||||
|
|
||||||
|
typedef struct Toy_AstAggregate {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* left;
|
||||||
|
Toy_Ast* right;
|
||||||
|
} Toy_AstAggregate;
|
||||||
|
|
||||||
|
typedef struct Toy_AstAssert {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* child;
|
||||||
|
Toy_Ast* message;
|
||||||
|
} Toy_AstAssert;
|
||||||
|
|
||||||
|
typedef struct Toy_AstIfThenElse {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* condBranch;
|
||||||
|
Toy_Ast* thenBranch;
|
||||||
|
Toy_Ast* elseBranch;
|
||||||
|
} Toy_AstIfThenElse;
|
||||||
|
|
||||||
|
typedef struct Toy_AstWhileThen {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* condBranch;
|
||||||
|
Toy_Ast* thenBranch;
|
||||||
|
} Toy_AstWhileThen;
|
||||||
|
|
||||||
|
typedef struct Toy_AstForThen {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* condBranch;
|
||||||
|
Toy_Ast* thenBranch;
|
||||||
|
} Toy_AstForThen;
|
||||||
|
|
||||||
|
typedef struct Toy_AstBreak {
|
||||||
|
Toy_AstType type;
|
||||||
|
} Toy_AstBreak;
|
||||||
|
|
||||||
|
typedef struct Toy_AstContinue {
|
||||||
|
Toy_AstType type;
|
||||||
|
} Toy_AstContinue;
|
||||||
|
|
||||||
|
typedef struct Toy_AstReturn {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstReturn;
|
||||||
|
|
||||||
|
typedef struct Toy_AstPrint {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstPrint;
|
||||||
|
|
||||||
|
typedef struct Toy_AstVarDeclare {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_String* name;
|
||||||
|
Toy_Ast* expr;
|
||||||
|
Toy_ValueType valueType;
|
||||||
|
bool constant;
|
||||||
|
} Toy_AstVarDeclare;
|
||||||
|
|
||||||
|
typedef struct Toy_AstVarAssign {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstFlag flag;
|
||||||
|
Toy_Ast* target;
|
||||||
|
Toy_Ast* expr;
|
||||||
|
} Toy_AstVarAssign;
|
||||||
|
|
||||||
|
typedef struct Toy_AstVarAccess {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstVarAccess;
|
||||||
|
|
||||||
|
typedef struct Toy_AstFnDeclare {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_String* name;
|
||||||
|
Toy_Ast* params;
|
||||||
|
Toy_Ast* body;
|
||||||
|
} Toy_AstFnDeclare;
|
||||||
|
|
||||||
|
typedef struct Toy_AstFnInvoke {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* function;
|
||||||
|
Toy_Ast* args;
|
||||||
|
} Toy_AstFnInvoke;
|
||||||
|
|
||||||
|
typedef struct Toy_AstAttribute {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* left;
|
||||||
|
Toy_Ast* right;
|
||||||
|
} Toy_AstAttribute;
|
||||||
|
|
||||||
|
typedef struct Toy_AstIterable {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* left;
|
||||||
|
Toy_Ast* right;
|
||||||
|
} Toy_AstIterable;
|
||||||
|
|
||||||
|
typedef struct Toy_AstStackPop {
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_Ast* child;
|
||||||
|
} Toy_AstStackPop;
|
||||||
|
|
||||||
|
typedef struct Toy_AstPass {
|
||||||
|
Toy_AstType type;
|
||||||
|
} Toy_AstPass;
|
||||||
|
|
||||||
|
typedef struct Toy_AstError {
|
||||||
|
Toy_AstType type;
|
||||||
|
} Toy_AstError;
|
||||||
|
|
||||||
|
typedef struct Toy_AstEnd {
|
||||||
|
Toy_AstType type;
|
||||||
|
} Toy_AstEnd;
|
||||||
|
|
||||||
|
union Toy_Ast { //see 'test_ast.c' for bitness tests
|
||||||
|
Toy_AstType type;
|
||||||
|
Toy_AstBlock block;
|
||||||
|
Toy_AstValue value;
|
||||||
|
Toy_AstUnary unary;
|
||||||
|
Toy_AstBinary binary;
|
||||||
|
Toy_AstBinaryShortCircuit binaryShortCircuit;
|
||||||
|
Toy_AstCompare compare;
|
||||||
|
Toy_AstGroup group;
|
||||||
|
Toy_AstCompound compound;
|
||||||
|
Toy_AstAggregate aggregate;
|
||||||
|
Toy_AstAssert assert;
|
||||||
|
Toy_AstIfThenElse ifThenElse;
|
||||||
|
Toy_AstWhileThen whileThen;
|
||||||
|
Toy_AstForThen forThen;
|
||||||
|
Toy_AstBreak breakPoint;
|
||||||
|
Toy_AstContinue continuePoint;
|
||||||
|
Toy_AstReturn fnReturn;
|
||||||
|
Toy_AstPrint print;
|
||||||
|
Toy_AstVarDeclare varDeclare;
|
||||||
|
Toy_AstVarAssign varAssign;
|
||||||
|
Toy_AstVarAccess varAccess;
|
||||||
|
Toy_AstFnDeclare fnDeclare;
|
||||||
|
Toy_AstFnInvoke fnInvoke;
|
||||||
|
Toy_AstAttribute attribute;
|
||||||
|
Toy_AstIterable iterable;
|
||||||
|
Toy_AstStackPop stackPop;
|
||||||
|
Toy_AstPass pass;
|
||||||
|
Toy_AstError error;
|
||||||
|
Toy_AstEnd end;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child);
|
||||||
|
|
||||||
|
void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value);
|
||||||
|
void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag);
|
||||||
|
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
|
||||||
|
void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
|
||||||
|
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
|
||||||
|
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag);
|
||||||
|
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
|
||||||
|
|
||||||
|
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
|
||||||
|
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);
|
||||||
|
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
|
||||||
|
void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
|
||||||
|
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||||
|
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||||
|
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
|
||||||
|
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr);
|
||||||
|
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr);
|
||||||
|
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
|
||||||
|
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body);
|
||||||
|
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params);
|
||||||
|
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
|
||||||
|
void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
|
||||||
|
|
||||||
|
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
|
||||||
|
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
|
||||||
|
const char* Toy_private_getAstTypeAsCString(Toy_AstType type);
|
||||||
@@ -1,433 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
#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);
|
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
#include "toy_attributes.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
//if set, used for delegating to user-defined code
|
||||||
|
static Toy_OpaqueAttributeHandler opaqueAttributeCallback = NULL;
|
||||||
|
|
||||||
|
//utils
|
||||||
|
#define MATCH_VALUE_AND_CSTRING(value, cstring) \
|
||||||
|
((TOY_VALUE_AS_STRING(value)->info.length == strlen(cstring)) && \
|
||||||
|
(strncmp(cstring, TOY_VALUE_AS_STRING(value)->leaf.data, TOY_VALUE_AS_STRING(value)->info.length) == 0))
|
||||||
|
|
||||||
|
//NOTE: there is no need to call 'Toy_freeValue' on the arguments, as the VM assumes you don't
|
||||||
|
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||||
|
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||||
|
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_STRING(compound)->info.length);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "asUpper")) {
|
||||||
|
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
|
||||||
|
for (int i = 0; buffer[i] != '\0'; i++) {
|
||||||
|
buffer[i] = toupper(buffer[i]);
|
||||||
|
}
|
||||||
|
Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer));
|
||||||
|
free(buffer);
|
||||||
|
return TOY_VALUE_FROM_STRING(str);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "asLower")) {
|
||||||
|
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
|
||||||
|
for (int i = 0; buffer[i] != '\0'; i++) {
|
||||||
|
buffer[i] = tolower(buffer[i]);
|
||||||
|
}
|
||||||
|
Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer));
|
||||||
|
free(buffer);
|
||||||
|
return TOY_VALUE_FROM_STRING(str);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
|
||||||
|
Toy_error(buffer);
|
||||||
|
return TOY_VALUE_FROM_NULL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_arrayPushBack(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||||
|
Toy_Value element = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
|
||||||
|
|
||||||
|
//BUGFIX: check the capacity limit
|
||||||
|
if (array->count == array->capacity) {
|
||||||
|
//correct the source value's pointer
|
||||||
|
array = Toy_resizeArray(array, array->capacity * TOY_ARRAY_EXPANSION_RATE);
|
||||||
|
if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_ARRAY) {
|
||||||
|
compound.as.reference->as.array = array;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Unknown error after expanding array size at %s %d", __FILE__, __LINE__);
|
||||||
|
Toy_error(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array->data[array->count] = element;
|
||||||
|
array->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_arrayPopBack(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
|
||||||
|
|
||||||
|
//empty returns nothing
|
||||||
|
if (array->count == 0) {
|
||||||
|
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value element = array->data[array->count-1];
|
||||||
|
array->count--;
|
||||||
|
|
||||||
|
Toy_pushStack(&vm->stack, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_arrayForEach(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
//URGENT: replace with for-loop
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||||
|
Toy_Value callback = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
if (TOY_VALUE_IS_FUNCTION(callback) != true) {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Expected function, found '%s'", Toy_getValueTypeAsCString(callback.type));
|
||||||
|
Toy_error(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
|
||||||
|
Toy_Function* fn = TOY_VALUE_AS_FUNCTION(callback);
|
||||||
|
|
||||||
|
//this emulates 'processInvoke' a bit, but not entirely
|
||||||
|
Toy_VM subVM;
|
||||||
|
Toy_inheritVM(vm, &subVM);
|
||||||
|
|
||||||
|
switch(fn->type) {
|
||||||
|
case TOY_FUNCTION_CUSTOM: {
|
||||||
|
//push and run for each element
|
||||||
|
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
|
||||||
|
//bind to the subVM (more expensive than I'd like)
|
||||||
|
Toy_bindVM(&subVM, fn->bytecode.code, fn->bytecode.parentScope);
|
||||||
|
|
||||||
|
//get parameter name as a string
|
||||||
|
unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0];
|
||||||
|
Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]);
|
||||||
|
const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr;
|
||||||
|
Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr));
|
||||||
|
|
||||||
|
Toy_declareScope(subVM.scope, Toy_copyString(name), paramType, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]), true);
|
||||||
|
Toy_freeString(name);
|
||||||
|
|
||||||
|
Toy_runVM(&subVM);
|
||||||
|
|
||||||
|
Toy_resetVM(&subVM, false, true);
|
||||||
|
subVM.scope = NULL; //BUGFIX: need to clear the scope when iterating
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOY_FUNCTION_NATIVE: {
|
||||||
|
//this uses a subVM for the native function, which is a slight difference than 'processInoke'
|
||||||
|
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
|
||||||
|
Toy_pushStack(&subVM.stack, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]));
|
||||||
|
|
||||||
|
fn->native.callback(&subVM, &fn->native); //NOTE: try not to leave anything on the stack afterwards
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Toy_error("Can't call an unknown function type in 'forEach'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeVM(&subVM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_arraySort(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)vm;
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
//URGENT: attr_arraySort
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||||
|
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||||
|
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "pushBack")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPushBack);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "popBack")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayForEach);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "sort")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
|
||||||
|
Toy_error(buffer);
|
||||||
|
return TOY_VALUE_FROM_NULL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_tableInsert(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||||
|
Toy_Value value = Toy_popStack(&vm->stack); //NOTE: the args are still backwards, except compound
|
||||||
|
Toy_Value key = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
|
||||||
|
Toy_insertTable(&table, key, value);
|
||||||
|
|
||||||
|
//BUGFIX: check the capacity limit (Toy_insertTable automatically alters the pointer value)
|
||||||
|
if (TOY_VALUE_AS_TABLE(compound) != table) {
|
||||||
|
//correct the source value's pointer
|
||||||
|
if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_TABLE) {
|
||||||
|
compound.as.reference->as.table = table;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Unknown error after expanding table size at %s %d", __FILE__, __LINE__);
|
||||||
|
Toy_error(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_tableHasKey(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||||
|
Toy_Value key = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
|
||||||
|
|
||||||
|
Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(&table, key);
|
||||||
|
Toy_Value result = TOY_VALUE_FROM_BOOLEAN(entry != NULL);
|
||||||
|
|
||||||
|
Toy_pushStack(&vm->stack, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_tableRemove(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||||
|
Toy_Value key = Toy_popStack(&vm->stack);
|
||||||
|
|
||||||
|
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
|
||||||
|
|
||||||
|
Toy_removeTable(&table, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_tableForEach(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||||
|
(void)vm;
|
||||||
|
(void)self;
|
||||||
|
|
||||||
|
//URGENT: replace with for-loop
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||||
|
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||||
|
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "insert")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableInsert);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "hasKey")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableHasKey);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "remove")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableRemove);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
|
||||||
|
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableForEach);
|
||||||
|
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
|
||||||
|
Toy_error(buffer);
|
||||||
|
return TOY_VALUE_FROM_NULL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Value Toy_private_handleOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||||
|
if (opaqueAttributeCallback == NULL) {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s' (did you set the opaque callbacks?)", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
|
||||||
|
Toy_error(buffer);
|
||||||
|
return TOY_VALUE_FROM_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
return opaqueAttributeCallback(vm, compound, attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb) {
|
||||||
|
opaqueAttributeCallback = cb;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "toy_value.h"
|
||||||
|
#include "toy_vm.h"
|
||||||
|
|
||||||
|
// [x] string.length
|
||||||
|
// [x] string.asUpper
|
||||||
|
// [x] string.asLower
|
||||||
|
// [x] array.length
|
||||||
|
// [x] array.pushBack(x)
|
||||||
|
// [x] array.popBack()
|
||||||
|
// [x] array.forEach(fn) // fn(x) -> void
|
||||||
|
// [ ] array.sort(fn) // fn(a,b) -> int
|
||||||
|
// [x] table.length
|
||||||
|
// [x] table.insert(x, y)
|
||||||
|
// [x] table.hasKey(x)
|
||||||
|
// [x] table.remove(x)
|
||||||
|
// [ ] table.forEach(fn) // fn(x,y) -> void
|
||||||
|
|
||||||
|
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
|
||||||
|
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
|
||||||
|
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
|
||||||
|
Toy_Value Toy_private_handleOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
|
||||||
|
|
||||||
|
//plug-and-play attributes for custom objects
|
||||||
|
typedef Toy_Value (*Toy_OpaqueAttributeHandler)(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
|
||||||
|
TOY_API void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb);
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#include "toy_bucket.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//buckets of fun
|
||||||
|
Toy_Bucket* Toy_allocateBucket(unsigned int capacity) {
|
||||||
|
assert(capacity != 0 && "Cannot allocate a 'Toy_Bucket' with zero capacity");
|
||||||
|
|
||||||
|
Toy_Bucket* bucket = malloc(sizeof(Toy_Bucket) + capacity);
|
||||||
|
|
||||||
|
if (bucket == NULL) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Bucket' of %d capacity\n" TOY_CC_RESET, (int)capacity);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(bucket, 0, sizeof(Toy_Bucket) + capacity); //zero the memory, to avoid broken header metadata
|
||||||
|
|
||||||
|
//initialize the bucket
|
||||||
|
bucket->next = NULL;
|
||||||
|
bucket->capacity = capacity;
|
||||||
|
bucket->count = 0;
|
||||||
|
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount) {
|
||||||
|
//the endpoint must be aligned to the word size, otherwise you'll get a bus error from moving pointers
|
||||||
|
amount = (amount + 3) & ~3; //NOTE: this also leaves the lowest two bits as zero
|
||||||
|
|
||||||
|
assert((*bucketHandle) != NULL && "Expected a 'Toy_Bucket', received NULL");
|
||||||
|
assert((*bucketHandle)->capacity >= (amount + 4) && "ERROR: Failed to partition a 'Toy_Bucket', requested amount is too high");
|
||||||
|
|
||||||
|
//if you're out of space in this bucket, allocate another one
|
||||||
|
if ((*bucketHandle)->capacity < (*bucketHandle)->count + amount + 4) { //+4 for the metadata header
|
||||||
|
Toy_Bucket* tmp = Toy_allocateBucket((*bucketHandle)->capacity);
|
||||||
|
tmp->next = (*bucketHandle); //it's buckets all the way down
|
||||||
|
(*bucketHandle) = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
//use a 4-byte metadata header to hold the size of this partition, for GC
|
||||||
|
*((unsigned int*)((*bucketHandle)->data + (*bucketHandle)->count)) = amount;
|
||||||
|
|
||||||
|
//track the new metadata, and return the requested memory space
|
||||||
|
(*bucketHandle)->count += amount + 4;
|
||||||
|
return ((*bucketHandle)->data + (*bucketHandle)->count - amount); //metadata is before the pointer's address
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_releaseBucketPartition(unsigned char* ptr) {
|
||||||
|
*((int*)(ptr-4)) |= 1; //flips the low-bit within the header
|
||||||
|
//no checks here, for technical reasons
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_freeBucket(Toy_Bucket** bucketHandle) {
|
||||||
|
Toy_Bucket* iter = (*bucketHandle);
|
||||||
|
|
||||||
|
while (iter != NULL) {
|
||||||
|
//run down the chain
|
||||||
|
Toy_Bucket* last = iter;
|
||||||
|
iter = iter->next;
|
||||||
|
|
||||||
|
//clear the previous bucket from memory
|
||||||
|
free(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
//for safety
|
||||||
|
(*bucketHandle) = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
|
||||||
|
//clear whatever this handle is pointing to
|
||||||
|
if ((*bucketHandle) == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Bucket* link = *bucketHandle;
|
||||||
|
while (link) {
|
||||||
|
//find non-free partitions
|
||||||
|
unsigned char* ptr = link->data;
|
||||||
|
|
||||||
|
bool gc = true;
|
||||||
|
|
||||||
|
while (ptr - link->data < link->count) { //for each partition
|
||||||
|
if ( (*((int*)ptr) & 1) == 0) { //is this partition still in use?
|
||||||
|
gc = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||||
|
}
|
||||||
|
|
||||||
|
//free this link, if its been entirely released
|
||||||
|
if (gc) {
|
||||||
|
//if link is the head
|
||||||
|
if (link == (*bucketHandle)) {
|
||||||
|
//if there's nowhere to go, don't delete the whole bucket
|
||||||
|
if ((*bucketHandle)->next == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(*bucketHandle) = (*bucketHandle)->next;
|
||||||
|
free(link);
|
||||||
|
link = (*bucketHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//find the prev and free this link before continuing
|
||||||
|
Toy_Bucket* it = (*bucketHandle);
|
||||||
|
while (it->next != link) {
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
it->next = link->next;
|
||||||
|
free(link);
|
||||||
|
link = it->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
link = link->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
//NOTE: this is an 'arena allocator', and has restrictions on it's usage:
|
||||||
|
// - It can only expand until it is freed
|
||||||
|
// - It cannot be copied or moved around in memory
|
||||||
|
// - It cannot allocate more memory than it has 'capacity'
|
||||||
|
// If each of these rules are followed, this is actually more efficient than other options
|
||||||
|
|
||||||
|
//a custom allocator
|
||||||
|
typedef struct Toy_Bucket { //32 | 64 BITNESS
|
||||||
|
struct Toy_Bucket* next; //4 | 8
|
||||||
|
unsigned int capacity; //4 | 4
|
||||||
|
unsigned int count; //4 | 4
|
||||||
|
unsigned char data[]; //- | -
|
||||||
|
} Toy_Bucket; //12 | 16
|
||||||
|
|
||||||
|
TOY_API Toy_Bucket* Toy_allocateBucket(unsigned int capacity);
|
||||||
|
TOY_API unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount);
|
||||||
|
TOY_API void Toy_releaseBucketPartition(unsigned char* ptr);
|
||||||
|
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);
|
||||||
|
|
||||||
|
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
|
||||||
|
|
||||||
|
//standard capacity sizes
|
||||||
|
#ifndef TOY_BUCKET_1KB
|
||||||
|
#define TOY_BUCKET_1KB (1 << 10)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_BUCKET_2KB
|
||||||
|
#define TOY_BUCKET_2KB (1 << 11)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_BUCKET_4KB
|
||||||
|
#define TOY_BUCKET_4KB (1 << 12)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_BUCKET_8KB
|
||||||
|
#define TOY_BUCKET_8KB (1 << 13)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_BUCKET_16KB
|
||||||
|
#define TOY_BUCKET_16KB (1 << 14)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_BUCKET_32KB
|
||||||
|
#define TOY_BUCKET_32KB (1 << 15)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_BUCKET_64KB
|
||||||
|
#define TOY_BUCKET_64KB (1 << 16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//CPU L1 caches tend to be 64kb, but that's far from guaranteed
|
||||||
|
#ifndef TOY_BUCKET_IDEAL
|
||||||
|
#define TOY_BUCKET_IDEAL (TOY_BUCKET_64KB - sizeof(Toy_Bucket))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//TODO: check for leaks when freeBucket is called, for debugging
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
|||||||
#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);
|
|
||||||
+3
-149
@@ -1,154 +1,8 @@
|
|||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
//defined separately, as compilation can take several seconds, invalidating the comparisons of the given macros
|
||||||
#include <string.h>
|
static const char* build = __DATE__ " " __TIME__ ", incomplete Toy v2.x";
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
//test variable sizes based on platform - see issue #35
|
const char* Toy_private_versionBuild(void) {
|
||||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(char) == 1);
|
|
||||||
STATIC_ASSERT(sizeof(short) == 2);
|
|
||||||
STATIC_ASSERT(sizeof(int) == 4);
|
|
||||||
STATIC_ASSERT(sizeof(float) == 4);
|
|
||||||
STATIC_ASSERT(sizeof(unsigned char) == 1);
|
|
||||||
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
|
||||||
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
|
||||||
|
|
||||||
static const char* build = __DATE__ " " __TIME__;
|
|
||||||
|
|
||||||
const char* Toy_private_version_build() {
|
|
||||||
return build;
|
return build;
|
||||||
}
|
}
|
||||||
|
|
||||||
//declare the singleton with default values
|
|
||||||
Toy_CommandLine Toy_commandLine = {
|
|
||||||
.error = false,
|
|
||||||
.help = false,
|
|
||||||
.version = false,
|
|
||||||
.binaryfile = NULL,
|
|
||||||
.sourcefile = NULL,
|
|
||||||
.compilefile = NULL,
|
|
||||||
.outfile = "out.tb",
|
|
||||||
.source = NULL,
|
|
||||||
.initialfile = NULL,
|
|
||||||
.enablePrintNewline = true,
|
|
||||||
.parseBytecodeHeader = false,
|
|
||||||
.verbose = false
|
|
||||||
};
|
|
||||||
|
|
||||||
void Toy_initCommandLine(int argc, const char* argv[]) {
|
|
||||||
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
|
||||||
Toy_commandLine.error = true; //error state by default, set to false by successful flags
|
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
|
||||||
Toy_commandLine.help = true;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
|
||||||
Toy_commandLine.version = true;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
|
||||||
Toy_commandLine.verbose = true;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
|
||||||
Toy_commandLine.sourcefile = (char*)argv[i + 1];
|
|
||||||
i++;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
|
||||||
Toy_commandLine.source = (char*)argv[i + 1];
|
|
||||||
i++;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
|
||||||
Toy_commandLine.compilefile = (char*)argv[i + 1];
|
|
||||||
i++;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
|
||||||
Toy_commandLine.outfile = (char*)argv[i + 1];
|
|
||||||
i++;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!strcmp(argv[i], "-t") || !strcmp(argv[i], "--initial")) && i + 1 < argc) {
|
|
||||||
Toy_commandLine.initialfile = (char*)argv[i + 1];
|
|
||||||
i++;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-p")) {
|
|
||||||
Toy_commandLine.parseBytecodeHeader = true;
|
|
||||||
|
|
||||||
if (Toy_commandLine.binaryfile) {
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(argv[i], "-n")) {
|
|
||||||
Toy_commandLine.enablePrintNewline = false;
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//option without a flag + ending in .tb = binary input
|
|
||||||
if (i < argc) {
|
|
||||||
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
|
||||||
Toy_commandLine.binaryfile = (char*)argv[i];
|
|
||||||
Toy_commandLine.error = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//don't keep reading in an error state
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_usageCommandLine(int argc, const char* argv[]) {
|
|
||||||
printf("Usage: %s [ file.tb | -h | -v | -d | -f file.toy | -i source | -c file.toy -o out.tb | -t file.toy ]\n\n", argv[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_helpCommandLine(int argc, const char* argv[]) {
|
|
||||||
Toy_usageCommandLine(argc, argv);
|
|
||||||
|
|
||||||
printf(" -h, --help\t\t\tShow this help then exit.\n");
|
|
||||||
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
|
|
||||||
printf(" -d, --debug\t\t\tBe verbose when operating.\n");
|
|
||||||
printf(" -f, --file filename\t\tParse, compile and execute the source file.\n");
|
|
||||||
printf(" -i, --input source\t\tParse, compile and execute this given string of source code.\n");
|
|
||||||
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
|
|
||||||
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
|
|
||||||
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
|
|
||||||
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
|
|
||||||
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
|
|
||||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
|
||||||
printf("Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios\n\n");
|
|
||||||
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
|
|
||||||
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
|
|
||||||
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
|
|
||||||
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
|
|
||||||
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
|
|
||||||
}
|
|
||||||
|
|||||||
+58
-93
@@ -1,109 +1,74 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*!
|
//for specified type sizes
|
||||||
# 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 <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
/*!
|
//TOY_API is platform-dependant, and marks publicly usable API functions
|
||||||
### TOY_API
|
#if defined(__linux__)
|
||||||
|
|
||||||
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__)
|
|
||||||
|
|
||||||
#define TOY_API extern
|
#define TOY_API extern
|
||||||
|
#elif defined(_WIN32) || defined(_WIN64)
|
||||||
#elif defined(_MSC_VER)
|
#if defined(TOY_EXPORT)
|
||||||
|
#define TOY_API __declspec(dllexport)
|
||||||
#ifndef TOY_EXPORT
|
#elif defined(TOY_IMPORT)
|
||||||
#define TOY_API __declspec(dllimport)
|
#define TOY_API __declspec(dllimport)
|
||||||
#else
|
#else
|
||||||
#define TOY_API __declspec(dllexport)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define TOY_API extern
|
#define TOY_API extern
|
||||||
|
#endif
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define TOY_API extern
|
||||||
|
#else
|
||||||
|
//generic solution
|
||||||
|
#define TOY_API extern
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*!
|
//TOY_BITNESS is used to encourage memory-cache friendliness
|
||||||
### TOY_VERSION_MAJOR
|
#if defined(__linux__)
|
||||||
|
#if defined(__LP64__)
|
||||||
|
#define TOY_BITNESS 64
|
||||||
|
#else
|
||||||
|
#define TOY_BITNESS 32
|
||||||
|
#endif
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
#if defined(__LP64__)
|
||||||
|
#define TOY_BITNESS 64
|
||||||
|
#else
|
||||||
|
#define TOY_BITNESS 32
|
||||||
|
#endif
|
||||||
|
#elif defined(_WIN32) || defined(_WIN64)
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#define TOY_BITNESS 64
|
||||||
|
#else
|
||||||
|
#define TOY_BITNESS 32
|
||||||
|
#endif
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#if defined(__LP64__)
|
||||||
|
#define TOY_BITNESS 64
|
||||||
|
#else
|
||||||
|
#define TOY_BITNESS 32
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
//generic solution
|
||||||
|
#define TOY_BITNESS -1
|
||||||
|
#endif
|
||||||
|
|
||||||
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.
|
//version specifiers, embedded as the header
|
||||||
|
#define TOY_VERSION_MAJOR 2
|
||||||
|
#define TOY_VERSION_MINOR 1
|
||||||
|
#define TOY_VERSION_PATCH 0
|
||||||
|
|
||||||
This value MUST fit into an unsigned char.
|
//defined as a function, for technical reasons
|
||||||
!*/
|
#define TOY_VERSION_BUILD Toy_private_versionBuild()
|
||||||
|
const char* Toy_private_versionBuild(void);
|
||||||
#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.
|
|
||||||
|
Version validation rules:
|
||||||
|
|
||||||
|
* Under no circumstance, should you ever run code whose major version is different from the interpreter’s major version
|
||||||
|
* Under no circumstance, should you ever run code whose minor version is above the interpreter’s minor version
|
||||||
|
* You may, at your own risk, attempt to run code whose patch version is different from the interpreter’s patch version
|
||||||
|
* You may, at your own risk, attempt to run code whose build version is different from the interpreter’s build version
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//for processing the command line arguments in the repl
|
|
||||||
typedef struct {
|
|
||||||
bool error;
|
|
||||||
bool help;
|
|
||||||
bool version;
|
|
||||||
char* binaryfile;
|
|
||||||
char* sourcefile;
|
|
||||||
char* compilefile;
|
|
||||||
char* outfile; //defaults to out.tb
|
|
||||||
char* source;
|
|
||||||
char* initialfile;
|
|
||||||
bool enablePrintNewline;
|
|
||||||
bool parseBytecodeHeader;
|
|
||||||
bool verbose;
|
|
||||||
} Toy_CommandLine;
|
|
||||||
|
|
||||||
//these are intended for the repl only, despite using the api prefix
|
|
||||||
TOY_API Toy_CommandLine Toy_commandLine;
|
|
||||||
|
|
||||||
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
|
|
||||||
|
|
||||||
TOY_API void Toy_usageCommandLine(int argc, const char* argv[]);
|
|
||||||
TOY_API void Toy_helpCommandLine(int argc, const char* argv[]);
|
|
||||||
TOY_API void Toy_copyrightCommandLine(int argc, const char* argv[]);
|
|
||||||
|
|||||||
+1317
-1334
File diff suppressed because it is too large
Load Diff
+54
-54
@@ -1,60 +1,60 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*!
|
|
||||||
# toy_compiler.h
|
|
||||||
|
|
||||||
This header defines the compiler structure, which is used to transform abstract syntax trees into usable intermediate bytecode. There are two steps to generating bytecode - the writing step, and the collation step.
|
|
||||||
|
|
||||||
During the writing step, the core of the program is generated, along with a series of literals representing the values within the program; these values are compressed and flattened into semi-unrecognizable forms. If the same literal is used multiple times in a program, such as a variable name, the name itself is replaced by a reference to the flattened literals within the cache.
|
|
||||||
|
|
||||||
During the collation step, everything from the core program’s execution instructions, the flattened literals, the functions (which have their own sections and protocols within the bytecode) and version information (such as the macros defined in toy_common.h) are all combined into a single buffer of bytes, known as bytecode. This bytecode can then be safely saved to a file or immediately executed.
|
|
||||||
!*/
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
#include "toy_opcodes.h"
|
#include "toy_ast.h"
|
||||||
#include "toy_ast_node.h"
|
|
||||||
#include "toy_literal_array.h"
|
|
||||||
|
|
||||||
typedef struct Toy_Compiler {
|
//the 'escapes' are lists of data used for processing the 'break' and 'continue' keywords
|
||||||
Toy_LiteralArray literalCache;
|
typedef struct Toy_private_EscapeEntry_t {
|
||||||
unsigned char* bytecode;
|
unsigned int addr; //the address to write *to*
|
||||||
int capacity;
|
unsigned int depth; //the current depth
|
||||||
int count;
|
} Toy_private_EscapeEntry_t;
|
||||||
|
|
||||||
|
typedef struct Toy_private_EscapeArray {
|
||||||
|
unsigned int capacity;
|
||||||
|
unsigned int count;
|
||||||
|
Toy_private_EscapeEntry_t data[];
|
||||||
|
} Toy_private_EscapeArray;
|
||||||
|
|
||||||
|
//not needed at runtime, so they can be bigger
|
||||||
|
#ifndef TOY_ESCAPE_INITIAL_CAPACITY
|
||||||
|
#define TOY_ESCAPE_INITIAL_CAPACITY 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_ESCAPE_EXPANSION_RATE
|
||||||
|
#define TOY_ESCAPE_EXPANSION_RATE 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Toy_private_EscapeArray* Toy_private_resizeEscapeArray(Toy_private_EscapeArray* ptr, unsigned int capacity);
|
||||||
|
|
||||||
|
//structure for holding the bytecode during compilation
|
||||||
|
typedef struct Toy_Bytecode {
|
||||||
|
unsigned char* code; //the instruction set
|
||||||
|
unsigned int codeCapacity;
|
||||||
|
unsigned int codeCount;
|
||||||
|
|
||||||
|
unsigned char* jumps; //each 'jump' is the starting address of an element within 'data'
|
||||||
|
unsigned int jumpsCapacity;
|
||||||
|
unsigned int jumpsCount;
|
||||||
|
|
||||||
|
unsigned char* param; //each 'param' is the starting address of a name string within 'data'
|
||||||
|
unsigned int paramCapacity;
|
||||||
|
unsigned int paramCount;
|
||||||
|
|
||||||
|
unsigned char* data; //a block of read-only data
|
||||||
|
unsigned int dataCapacity;
|
||||||
|
unsigned int dataCount;
|
||||||
|
|
||||||
|
unsigned char* subs; //subroutines etc, built recursively
|
||||||
|
unsigned int subsCapacity;
|
||||||
|
unsigned int subsCount;
|
||||||
|
|
||||||
|
//tools for handling the build process
|
||||||
|
unsigned int currentScopeDepth;
|
||||||
|
Toy_private_EscapeArray* breakEscapes;
|
||||||
|
Toy_private_EscapeArray* continueEscapes;
|
||||||
|
|
||||||
|
//compilation errors
|
||||||
bool panic;
|
bool panic;
|
||||||
} Toy_Compiler;
|
} Toy_Bytecode;
|
||||||
|
|
||||||
/*!
|
TOY_API unsigned char* Toy_compileToBytecode(Toy_Ast* ast);
|
||||||
## 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);
|
|
||||||
|
|||||||
+39
-31
@@ -8,7 +8,7 @@ a printf()'s first argument, like so:
|
|||||||
|
|
||||||
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
|
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
|
||||||
|
|
||||||
NOTE: you need both font AND background for these to work
|
reference: https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -16,32 +16,36 @@ NOTE: you need both font AND background for these to work
|
|||||||
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
|
||||||
|
|
||||||
//fonts color
|
//fonts color
|
||||||
#define TOY_CC_FONT_BLACK "\033[30;"
|
#define TOY_CC_FONT_BLACK "30"
|
||||||
#define TOY_CC_FONT_RED "\033[31;"
|
#define TOY_CC_FONT_RED "31"
|
||||||
#define TOY_CC_FONT_GREEN "\033[32;"
|
#define TOY_CC_FONT_GREEN "32"
|
||||||
#define TOY_CC_FONT_YELLOW "\033[33;"
|
#define TOY_CC_FONT_YELLOW "33"
|
||||||
#define TOY_CC_FONT_BLUE "\033[34;"
|
#define TOY_CC_FONT_BLUE "34"
|
||||||
#define TOY_CC_FONT_PURPLE "\033[35;"
|
#define TOY_CC_FONT_MAGENTA "35"
|
||||||
#define TOY_CC_FONT_DGREEN "\033[6;"
|
#define TOY_CC_FONT_CYAN "36"
|
||||||
#define TOY_CC_FONT_WHITE "\033[7;"
|
#define TOY_CC_FONT_WHITE "37"
|
||||||
#define TOY_CC_FONT_CYAN "\x1b[36m"
|
#define TOY_CC_FONT_DEFAULT "39"
|
||||||
|
|
||||||
//background color
|
//background color
|
||||||
#define TOY_CC_BACK_BLACK "40m"
|
#define TOY_CC_BACK_BLACK "40"
|
||||||
#define TOY_CC_BACK_RED "41m"
|
#define TOY_CC_BACK_RED "41"
|
||||||
#define TOY_CC_BACK_GREEN "42m"
|
#define TOY_CC_BACK_GREEN "42"
|
||||||
#define TOY_CC_BACK_YELLOW "43m"
|
#define TOY_CC_BACK_YELLOW "43"
|
||||||
#define TOY_CC_BACK_BLUE "44m"
|
#define TOY_CC_BACK_BLUE "44"
|
||||||
#define TOY_CC_BACK_PURPLE "45m"
|
#define TOY_CC_BACK_MAGENTA "45"
|
||||||
#define TOY_CC_BACK_DGREEN "46m"
|
#define TOY_CC_BACK_CYAN "46"
|
||||||
#define TOY_CC_BACK_WHITE "47m"
|
#define TOY_CC_BACK_WHITE "47"
|
||||||
|
#define TOY_CC_BACK_DEFAULT "49"
|
||||||
|
|
||||||
//useful
|
//useful macros
|
||||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
#define TOY_CC_DEBUG "\033[" TOY_CC_FONT_BLUE ";" TOY_CC_BACK_DEFAULT "m"
|
||||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
#define TOY_CC_NOTICE "\033[" TOY_CC_FONT_GREEN ";" TOY_CC_BACK_DEFAULT "m"
|
||||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
#define TOY_CC_WARN "\033[" TOY_CC_FONT_YELLOW ";" TOY_CC_BACK_DEFAULT "m"
|
||||||
#define TOY_CC_RESET "\033[0m"
|
#define TOY_CC_ERROR "\033[" TOY_CC_FONT_RED ";" TOY_CC_BACK_DEFAULT "m"
|
||||||
|
#define TOY_CC_ASSERT "\033[" TOY_CC_FONT_BLACK ";" TOY_CC_BACK_MAGENTA "m"
|
||||||
|
#define TOY_CC_RESET "\033[" TOY_CC_FONT_DEFAULT ";" TOY_CC_BACK_DEFAULT "m"
|
||||||
|
|
||||||
|
//for unsupported platforms, these become no-ops
|
||||||
#else
|
#else
|
||||||
|
|
||||||
//fonts color
|
//fonts color
|
||||||
@@ -50,10 +54,10 @@ NOTE: you need both font AND background for these to work
|
|||||||
#define TOY_CC_FONT_GREEN
|
#define TOY_CC_FONT_GREEN
|
||||||
#define TOY_CC_FONT_YELLOW
|
#define TOY_CC_FONT_YELLOW
|
||||||
#define TOY_CC_FONT_BLUE
|
#define TOY_CC_FONT_BLUE
|
||||||
#define TOY_CC_FONT_PURPLE
|
#define TOY_CC_FONT_MAGENTA
|
||||||
#define TOY_CC_FONT_DGREEN
|
|
||||||
#define TOY_CC_FONT_WHITE
|
|
||||||
#define TOY_CC_FONT_CYAN
|
#define TOY_CC_FONT_CYAN
|
||||||
|
#define TOY_CC_FONT_WHITE
|
||||||
|
#define TOY_CC_FONT_DEFAULT
|
||||||
|
|
||||||
//background color
|
//background color
|
||||||
#define TOY_CC_BACK_BLACK
|
#define TOY_CC_BACK_BLACK
|
||||||
@@ -61,14 +65,18 @@ NOTE: you need both font AND background for these to work
|
|||||||
#define TOY_CC_BACK_GREEN
|
#define TOY_CC_BACK_GREEN
|
||||||
#define TOY_CC_BACK_YELLOW
|
#define TOY_CC_BACK_YELLOW
|
||||||
#define TOY_CC_BACK_BLUE
|
#define TOY_CC_BACK_BLUE
|
||||||
#define TOY_CC_BACK_PURPLE
|
#define TOY_CC_BACK_MAGENTA
|
||||||
#define TOY_CC_BACK_DGREEN
|
#define TOY_CC_BACK_CYAN
|
||||||
#define TOY_CC_BACK_WHITE
|
#define TOY_CC_BACK_WHITE
|
||||||
|
#define TOY_CC_BACK_DEFAULT
|
||||||
|
|
||||||
//useful
|
//useful
|
||||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
#define TOY_CC_DEBUG
|
||||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
#define TOY_CC_NOTICE
|
||||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
#define TOY_CC_WARN
|
||||||
|
#define TOY_CC_ERROR
|
||||||
|
#define TOY_CC_ASSERT
|
||||||
#define TOY_CC_RESET
|
#define TOY_CC_RESET
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#include "toy_function.h"
|
||||||
|
|
||||||
|
Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope) {
|
||||||
|
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
|
||||||
|
|
||||||
|
fn->type = TOY_FUNCTION_CUSTOM;
|
||||||
|
fn->bytecode.code = bytecode;
|
||||||
|
fn->bytecode.parentScope = parentScope;
|
||||||
|
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback) {
|
||||||
|
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
|
||||||
|
|
||||||
|
fn->type = TOY_FUNCTION_NATIVE;
|
||||||
|
fn->native.callback = callback;
|
||||||
|
fn->native.meta1 = 0; //BUGFIX: Workaround for native functions lacking access to a closure-like scope
|
||||||
|
fn->native.meta2 = 0;
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* original) {
|
||||||
|
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
|
||||||
|
|
||||||
|
switch(original->type) {
|
||||||
|
case TOY_FUNCTION_CUSTOM: {
|
||||||
|
fn->type = original->type;
|
||||||
|
fn->bytecode.code = original->bytecode.code;
|
||||||
|
fn->bytecode.parentScope = original->bytecode.parentScope;
|
||||||
|
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOY_FUNCTION_NATIVE: {
|
||||||
|
fn->type = original->type;
|
||||||
|
fn->native.callback = original->native.callback;
|
||||||
|
fn->native.meta1 = original->native.meta1;
|
||||||
|
fn->native.meta2 = original->native.meta2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOY_API void Toy_freeFunction(Toy_Function* fn) {
|
||||||
|
if (fn->type == TOY_FUNCTION_CUSTOM) {
|
||||||
|
Toy_private_decrementScopeRefCount(fn->bytecode.parentScope);
|
||||||
|
}
|
||||||
|
else if (fn->type == TOY_FUNCTION_NATIVE) {
|
||||||
|
fn->native.callback = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_releaseBucketPartition((void*)fn);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "toy_bucket.h"
|
||||||
|
#include "toy_scope.h"
|
||||||
|
#include "toy_vm.h"
|
||||||
|
|
||||||
|
//forward declare
|
||||||
|
struct Toy_VM;
|
||||||
|
struct Toy_FunctionNative;
|
||||||
|
|
||||||
|
typedef void (*Toy_nativeCallback)(struct Toy_VM*, struct Toy_FunctionNative* self);
|
||||||
|
|
||||||
|
typedef enum Toy_FunctionType {
|
||||||
|
TOY_FUNCTION_CUSTOM,
|
||||||
|
TOY_FUNCTION_NATIVE,
|
||||||
|
} Toy_FunctionType;
|
||||||
|
|
||||||
|
typedef struct Toy_FunctionBytecode {
|
||||||
|
Toy_FunctionType type;
|
||||||
|
unsigned char* code;
|
||||||
|
Toy_Scope* parentScope;
|
||||||
|
} Toy_FunctionBytecode;
|
||||||
|
|
||||||
|
typedef struct Toy_FunctionNative {
|
||||||
|
Toy_FunctionType type;
|
||||||
|
Toy_nativeCallback callback;
|
||||||
|
int meta1;
|
||||||
|
int meta2;
|
||||||
|
} Toy_FunctionNative;
|
||||||
|
|
||||||
|
typedef union Toy_Function_t {
|
||||||
|
Toy_FunctionType type;
|
||||||
|
Toy_FunctionBytecode bytecode;
|
||||||
|
Toy_FunctionNative native;
|
||||||
|
} Toy_Function;
|
||||||
|
|
||||||
|
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope);
|
||||||
|
TOY_API Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback);
|
||||||
|
|
||||||
|
TOY_API Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* fn);
|
||||||
|
TOY_API void Toy_freeFunction(Toy_Function* fn);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
|||||||
#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);
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#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);
|
|
||||||
+163
-120
@@ -1,18 +1,94 @@
|
|||||||
#include "toy_lexer.h"
|
#include "toy_lexer.h"
|
||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
#include "toy_keyword_types.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
//keyword data
|
||||||
|
typedef struct {
|
||||||
|
const Toy_TokenType type;
|
||||||
|
const char* keyword;
|
||||||
|
} Toy_KeywordTypeTuple;
|
||||||
|
|
||||||
|
const Toy_KeywordTypeTuple keywordTuples[] = {
|
||||||
|
//null
|
||||||
|
{TOY_TOKEN_NULL, "null"},
|
||||||
|
|
||||||
|
//types
|
||||||
|
{TOY_TOKEN_TYPE_BOOLEAN, "Bool"},
|
||||||
|
{TOY_TOKEN_TYPE_INTEGER, "Int"},
|
||||||
|
{TOY_TOKEN_TYPE_FLOAT, "Float"},
|
||||||
|
{TOY_TOKEN_TYPE_STRING, "String"},
|
||||||
|
{TOY_TOKEN_TYPE_ARRAY, "Array"},
|
||||||
|
{TOY_TOKEN_TYPE_TABLE, "Table"},
|
||||||
|
{TOY_TOKEN_TYPE_FUNCTION, "Function"},
|
||||||
|
{TOY_TOKEN_TYPE_OPAQUE, "Opaque"},
|
||||||
|
{TOY_TOKEN_TYPE_ANY, "Any"},
|
||||||
|
|
||||||
|
//keywords and reserved words
|
||||||
|
{TOY_TOKEN_KEYWORD_AS, "as"},
|
||||||
|
{TOY_TOKEN_KEYWORD_ASSERT, "assert"},
|
||||||
|
{TOY_TOKEN_KEYWORD_BREAK, "break"},
|
||||||
|
{TOY_TOKEN_KEYWORD_CLASS, "class"},
|
||||||
|
{TOY_TOKEN_KEYWORD_CONST, "const"},
|
||||||
|
{TOY_TOKEN_KEYWORD_CONTINUE, "continue"},
|
||||||
|
{TOY_TOKEN_KEYWORD_DO, "do"},
|
||||||
|
{TOY_TOKEN_KEYWORD_ELSE, "else"},
|
||||||
|
{TOY_TOKEN_KEYWORD_EXPORT, "export"},
|
||||||
|
{TOY_TOKEN_KEYWORD_FOR, "for"},
|
||||||
|
{TOY_TOKEN_KEYWORD_FOREACH, "foreach"},
|
||||||
|
{TOY_TOKEN_KEYWORD_FUNCTION, "fn"},
|
||||||
|
{TOY_TOKEN_KEYWORD_IF, "if"},
|
||||||
|
{TOY_TOKEN_KEYWORD_IMPORT, "import"},
|
||||||
|
{TOY_TOKEN_KEYWORD_IN, "in"},
|
||||||
|
{TOY_TOKEN_KEYWORD_OF, "of"},
|
||||||
|
{TOY_TOKEN_KEYWORD_PASS, "pass"},
|
||||||
|
{TOY_TOKEN_KEYWORD_PRINT, "print"},
|
||||||
|
{TOY_TOKEN_KEYWORD_RETURN, "return"},
|
||||||
|
{TOY_TOKEN_KEYWORD_VAR, "var"},
|
||||||
|
{TOY_TOKEN_KEYWORD_WHILE, "while"},
|
||||||
|
{TOY_TOKEN_KEYWORD_YIELD, "yield"},
|
||||||
|
|
||||||
|
//literal values
|
||||||
|
{TOY_TOKEN_LITERAL_TRUE, "true"},
|
||||||
|
{TOY_TOKEN_LITERAL_FALSE, "false"},
|
||||||
|
|
||||||
|
{TOY_TOKEN_EOF, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* Toy_private_findKeywordByType(const Toy_TokenType type) {
|
||||||
|
if (type == TOY_TOKEN_EOF) {
|
||||||
|
return "EOF";
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; keywordTuples[i].keyword; i++) {
|
||||||
|
if (keywordTuples[i].type == type) {
|
||||||
|
return keywordTuples[i].keyword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_TokenType Toy_private_findTypeByKeyword(const char* keyword) {
|
||||||
|
const int length = strlen(keyword);
|
||||||
|
|
||||||
|
for (int i = 0; keywordTuples[i].keyword; i++) {
|
||||||
|
if (!strncmp(keyword, keywordTuples[i].keyword, length)) {
|
||||||
|
return keywordTuples[i].type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TOY_TOKEN_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
//static generic utility functions
|
//static generic utility functions
|
||||||
static void cleanLexer(Toy_Lexer* lexer) {
|
static void cleanLexer(Toy_Lexer* lexer) {
|
||||||
lexer->source = NULL;
|
|
||||||
lexer->start = 0;
|
lexer->start = 0;
|
||||||
lexer->current = 0;
|
lexer->current = 0;
|
||||||
lexer->line = 1;
|
lexer->line = 1;
|
||||||
lexer->commentsEnabled = true;
|
lexer->source = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isAtEnd(Toy_Lexer* lexer) {
|
static bool isAtEnd(Toy_Lexer* lexer) {
|
||||||
@@ -55,10 +131,6 @@ static void eatWhitespace(Toy_Lexer* lexer) {
|
|||||||
|
|
||||||
//comments
|
//comments
|
||||||
case '/':
|
case '/':
|
||||||
if (!lexer->commentsEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//eat the line
|
//eat the line
|
||||||
if (peekNext(lexer) == '/') {
|
if (peekNext(lexer) == '/') {
|
||||||
while (!isAtEnd(lexer) && advance(lexer) != '\n');
|
while (!isAtEnd(lexer) && advance(lexer) != '\n');
|
||||||
@@ -110,16 +182,9 @@ static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
|
|||||||
Toy_Token token;
|
Toy_Token token;
|
||||||
|
|
||||||
token.type = TOY_TOKEN_ERROR;
|
token.type = TOY_TOKEN_ERROR;
|
||||||
token.lexeme = msg;
|
|
||||||
token.length = strlen(msg);
|
token.length = strlen(msg);
|
||||||
token.line = lexer->line;
|
token.line = lexer->line;
|
||||||
|
token.lexeme = msg;
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (Toy_commandLine.verbose) {
|
|
||||||
printf("err:");
|
|
||||||
Toy_private_printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -129,48 +194,33 @@ static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
|||||||
|
|
||||||
token.type = type;
|
token.type = type;
|
||||||
token.length = lexer->current - lexer->start;
|
token.length = lexer->current - lexer->start;
|
||||||
token.lexeme = &lexer->source[lexer->current - token.length];
|
|
||||||
token.line = lexer->line;
|
token.line = lexer->line;
|
||||||
|
token.lexeme = &lexer->source[lexer->current - token.length];
|
||||||
#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;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||||
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
|
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //assume we're reading an integer
|
||||||
|
|
||||||
|
//the character '_' can be inserted into numbers as a separator
|
||||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||||
|
|
||||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //peekNext(lexer) == digit
|
||||||
type = TOY_TOKEN_LITERAL_FLOAT;
|
type = TOY_TOKEN_LITERAL_FLOAT; //change the assumption to reading a float
|
||||||
advance(lexer);
|
advance(lexer); //eat the '.'
|
||||||
|
|
||||||
|
//'_' again
|
||||||
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//make the token
|
||||||
Toy_Token token;
|
Toy_Token token;
|
||||||
|
|
||||||
token.type = type;
|
token.type = type;
|
||||||
token.lexeme = &lexer->source[lexer->start];
|
|
||||||
token.length = lexer->current - lexer->start;
|
token.length = lexer->current - lexer->start;
|
||||||
token.line = lexer->line;
|
token.line = lexer->line;
|
||||||
|
token.lexeme = &lexer->source[lexer->start];
|
||||||
#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;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -192,7 +242,6 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
|||||||
while (!isAtEnd(lexer)) {
|
while (!isAtEnd(lexer)) {
|
||||||
//stop if you've hit the terminator
|
//stop if you've hit the terminator
|
||||||
if (peek(lexer) == terminator) {
|
if (peek(lexer) == terminator) {
|
||||||
advance(lexer); //eat terminator
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,24 +260,20 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
|||||||
return makeErrorToken(lexer, "Unterminated string");
|
return makeErrorToken(lexer, "Unterminated string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
advance(lexer); //eat the terminator
|
||||||
|
|
||||||
|
//make the token
|
||||||
Toy_Token token;
|
Toy_Token token;
|
||||||
|
|
||||||
token.type = TOY_TOKEN_LITERAL_STRING;
|
token.type = TOY_TOKEN_LITERAL_STRING;
|
||||||
token.lexeme = &lexer->source[lexer->start + 1];
|
token.length = lexer->current - lexer->start - 2; //-1 to omit the quotes
|
||||||
token.length = lexer->current - lexer->start - 2;
|
|
||||||
token.line = lexer->line;
|
token.line = lexer->line;
|
||||||
|
token.lexeme = &lexer->source[lexer->start + 1]; //+1 to omit the first quote
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (Toy_commandLine.verbose) {
|
|
||||||
printf("str:");
|
|
||||||
Toy_private_printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
static Toy_Token makeKeywordOrName(Toy_Lexer* lexer) {
|
||||||
advance(lexer); //first letter can only be alpha
|
advance(lexer); //first letter can only be alpha
|
||||||
|
|
||||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||||
@@ -236,52 +281,43 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//scan for a keyword
|
//scan for a keyword
|
||||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
for (int i = 0; keywordTuples[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)) {
|
//WONTFIX: could squeeze miniscule performance gain from this, but ROI isn't worth it
|
||||||
|
if (strlen(keywordTuples[i].keyword) == (lexer->current - lexer->start) && !strncmp(keywordTuples[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||||
|
//make token (keyword)
|
||||||
Toy_Token token;
|
Toy_Token token;
|
||||||
|
|
||||||
token.type = Toy_keywordTypes[i].type;
|
token.type = keywordTuples[i].type;
|
||||||
token.lexeme = &lexer->source[lexer->start];
|
|
||||||
token.length = lexer->current - lexer->start;
|
token.length = lexer->current - lexer->start;
|
||||||
token.line = lexer->line;
|
token.line = lexer->line;
|
||||||
|
token.lexeme = &lexer->source[lexer->start];
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (Toy_commandLine.verbose) {
|
|
||||||
printf("kwd:");
|
|
||||||
Toy_private_printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//return an identifier
|
//make token (variable name)
|
||||||
Toy_Token token;
|
Toy_Token token;
|
||||||
|
|
||||||
token.type = TOY_TOKEN_IDENTIFIER;
|
token.type = TOY_TOKEN_NAME;
|
||||||
token.lexeme = &lexer->source[lexer->start];
|
|
||||||
token.length = lexer->current - lexer->start;
|
token.length = lexer->current - lexer->start;
|
||||||
token.line = lexer->line;
|
token.line = lexer->line;
|
||||||
|
token.lexeme = &lexer->source[lexer->start];
|
||||||
#ifndef TOY_EXPORT
|
|
||||||
if (Toy_commandLine.verbose) {
|
|
||||||
printf("idf:");
|
|
||||||
Toy_private_printToken(&token);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
|
void Toy_bindLexer(Toy_Lexer* lexer, const char* source) {
|
||||||
cleanLexer(lexer);
|
cleanLexer(lexer);
|
||||||
|
|
||||||
lexer->source = source;
|
lexer->source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
||||||
|
if (lexer->source == NULL) {
|
||||||
|
return makeErrorToken(lexer, "Missing source code in lexer");
|
||||||
|
}
|
||||||
|
|
||||||
eatWhitespace(lexer);
|
eatWhitespace(lexer);
|
||||||
|
|
||||||
lexer->start = lexer->current;
|
lexer->start = lexer->current;
|
||||||
@@ -289,95 +325,102 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
|||||||
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
|
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
|
||||||
|
|
||||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
if (isAlpha(lexer)) return makeKeywordOrName(lexer);
|
||||||
|
|
||||||
char c = advance(lexer);
|
char c = advance(lexer);
|
||||||
|
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case '(': return makeToken(lexer, TOY_TOKEN_PAREN_LEFT);
|
case '(': return makeToken(lexer, TOY_TOKEN_OPERATOR_PAREN_LEFT);
|
||||||
case ')': return makeToken(lexer, TOY_TOKEN_PAREN_RIGHT);
|
case ')': return makeToken(lexer, TOY_TOKEN_OPERATOR_PAREN_RIGHT);
|
||||||
case '{': return makeToken(lexer, TOY_TOKEN_BRACE_LEFT);
|
case '[': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACKET_LEFT);
|
||||||
case '}': return makeToken(lexer, TOY_TOKEN_BRACE_RIGHT);
|
case ']': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACKET_RIGHT);
|
||||||
case '[': return makeToken(lexer, TOY_TOKEN_BRACKET_LEFT);
|
case '{': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACE_LEFT);
|
||||||
case ']': return makeToken(lexer, TOY_TOKEN_BRACKET_RIGHT);
|
case '}': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACE_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_OPERATOR_ADD_ASSIGN : match(lexer, '+') ? TOY_TOKEN_OPERATOR_INCREMENT : TOY_TOKEN_OPERATOR_ADD);
|
||||||
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_OPERATOR_SUBTRACT_ASSIGN : match(lexer, '-') ? TOY_TOKEN_OPERATOR_DECREMENT : TOY_TOKEN_OPERATOR_SUBTRACT);
|
||||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MULTIPLY_ASSIGN : TOY_TOKEN_MULTIPLY);
|
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN : TOY_TOKEN_OPERATOR_MULTIPLY);
|
||||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_DIVIDE_ASSIGN : TOY_TOKEN_DIVIDE);
|
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN : TOY_TOKEN_OPERATOR_DIVIDE);
|
||||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MODULO_ASSIGN : TOY_TOKEN_MODULO);
|
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_MODULO_ASSIGN : TOY_TOKEN_OPERATOR_MODULO);
|
||||||
|
|
||||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_NOT_EQUAL : TOY_TOKEN_NOT);
|
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_NOT : TOY_TOKEN_OPERATOR_NEGATE);
|
||||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_EQUAL : TOY_TOKEN_ASSIGN);
|
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_EQUAL : TOY_TOKEN_OPERATOR_ASSIGN);
|
||||||
|
|
||||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_LESS_EQUAL : TOY_TOKEN_LESS);
|
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_LESS_EQUAL : TOY_TOKEN_OPERATOR_COMPARE_LESS);
|
||||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_GREATER_EQUAL : TOY_TOKEN_GREATER);
|
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL : TOY_TOKEN_OPERATOR_COMPARE_GREATER);
|
||||||
|
|
||||||
case '&': //TOKEN_AND not used
|
case '&': //TOY_TOKEN_OPERATOR_AMPERSAND is unused
|
||||||
if (advance(lexer) != '&') {
|
if (match(lexer, '&')) {
|
||||||
return makeErrorToken(lexer, "Unexpected '&'");
|
return makeToken(lexer, TOY_TOKEN_OPERATOR_AND);
|
||||||
} else {
|
} else {
|
||||||
return makeToken(lexer, TOY_TOKEN_AND_AND);
|
return makeErrorToken(lexer, "Unexpected '&'");
|
||||||
}
|
}
|
||||||
|
|
||||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR_OR : TOY_TOKEN_PIPE);
|
case '|': //TOY_TOKEN_OPERATOR_PIPE is unused
|
||||||
|
if (match(lexer, '|')) {
|
||||||
|
return makeToken(lexer, TOY_TOKEN_OPERATOR_OR);
|
||||||
|
} else {
|
||||||
|
return makeErrorToken(lexer, "Unexpected '|'");
|
||||||
|
}
|
||||||
|
|
||||||
|
case '?': return makeToken(lexer, TOY_TOKEN_OPERATOR_QUESTION);
|
||||||
|
case ':': return makeToken(lexer, TOY_TOKEN_OPERATOR_COLON);
|
||||||
|
case ';': return makeToken(lexer, TOY_TOKEN_OPERATOR_SEMICOLON);
|
||||||
|
case ',': return makeToken(lexer, TOY_TOKEN_OPERATOR_COMMA);
|
||||||
|
|
||||||
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 '.':
|
case '.':
|
||||||
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
if (match(lexer, '.')) {
|
||||||
advance(lexer);
|
if (match(lexer, '.')) {
|
||||||
advance(lexer);
|
return makeToken(lexer, TOY_TOKEN_OPERATOR_REST); //three dots
|
||||||
return makeToken(lexer, TOY_TOKEN_REST);
|
}
|
||||||
|
else {
|
||||||
|
return makeToken(lexer, TOY_TOKEN_OPERATOR_CONCAT); //two dots
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return makeToken(lexer, TOY_TOKEN_OPERATOR_DOT); //one dot
|
||||||
}
|
}
|
||||||
return makeToken(lexer, TOY_TOKEN_DOT);
|
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
return makeString(lexer, c);
|
return makeString(lexer, c);
|
||||||
//TODO: possibly support interpolated strings
|
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
char buffer[128];
|
return makeErrorToken(lexer, "Unknown token value found in lexer");
|
||||||
snprintf(buffer, 128, "Unexpected token: %c", c);
|
|
||||||
return makeErrorToken(lexer, buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void trim(char** s, int* l) { //all this to remove a newline?
|
static void trim(char** s, unsigned int* l) { //util
|
||||||
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
||||||
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
||||||
}
|
}
|
||||||
|
|
||||||
//for debugging
|
//for debugging
|
||||||
void Toy_private_printToken(Toy_Token* token) {
|
void Toy_private_printToken(Toy_Token* token) {
|
||||||
|
//print errors
|
||||||
if (token->type == TOY_TOKEN_ERROR) {
|
if (token->type == TOY_TOKEN_ERROR) {
|
||||||
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
|
printf(TOY_CC_ERROR "ERROR: \t%d\t%.*s\n" TOY_CC_RESET, (int)token->line, (int)token->length, token->lexeme);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\t%d\t%d\t", token->type, token->line);
|
//print the line number
|
||||||
|
printf("\t%d\t%d\t", token->type, (int)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) {
|
//print based on type
|
||||||
printf("%.*s\t", token->length, token->lexeme);
|
if (token->type == TOY_TOKEN_NAME || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
|
||||||
|
printf("%.*s\t", (int)token->length, token->lexeme);
|
||||||
} else {
|
} else {
|
||||||
char* keyword = Toy_findKeywordByType(token->type);
|
const char* keyword = Toy_private_findKeywordByType(token->type);
|
||||||
|
|
||||||
if (keyword != NULL) {
|
if (keyword != NULL) {
|
||||||
printf("%s", keyword);
|
printf("%s", keyword);
|
||||||
} else {
|
} else {
|
||||||
char* str = (char*)token->lexeme; //strip const-ness for trimming
|
char* str = (char*)token->lexeme; //strip const-ness for trimming
|
||||||
int length = token->length;
|
unsigned int length = token->length;
|
||||||
trim(&str, &length);
|
trim(&str, &length);
|
||||||
printf("%.*s", length, str);
|
printf("%.*s", (int)length, str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
|
|
||||||
lexer->commentsEnabled = enabled;
|
|
||||||
}
|
|
||||||
+11
-48
@@ -1,65 +1,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*!
|
|
||||||
# toy_lexer.h
|
|
||||||
|
|
||||||
This header defines the lexer and token structures, which can be bound to a piece of source code, and used to tokenize it within a parser.
|
|
||||||
!*/
|
|
||||||
|
|
||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
#include "toy_token_types.h"
|
#include "toy_token_types.h"
|
||||||
|
|
||||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
//lexers are bound to a string of code
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
unsigned int start; //start of the current token
|
||||||
|
unsigned int current; //current position of the lexer
|
||||||
|
unsigned int line; //track this for error handling
|
||||||
const char* source;
|
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;
|
} Toy_Lexer;
|
||||||
|
|
||||||
//tokens are intermediaries between lexers and parsers
|
//tokens are intermediaries between lexers and parsers
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Toy_TokenType type;
|
Toy_TokenType type;
|
||||||
|
unsigned int length;
|
||||||
|
unsigned int line;
|
||||||
const char* lexeme;
|
const char* lexeme;
|
||||||
int length;
|
|
||||||
int line;
|
|
||||||
} Toy_Token;
|
} Toy_Token;
|
||||||
|
|
||||||
/*!
|
TOY_API void Toy_bindLexer(Toy_Lexer* lexer, const char* source);
|
||||||
## Defined Functions
|
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
|
||||||
!*/
|
|
||||||
|
|
||||||
/*!
|
const char* Toy_private_findKeywordByType(const Toy_TokenType type);
|
||||||
### void Toy_initLexer(Toy_Lexer* lexer, const char* source)
|
Toy_TokenType Toy_private_findTypeByKeyword(const char* keyword);
|
||||||
|
void Toy_private_printToken(Toy_Token* token);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|||||||
@@ -1,737 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,380 +0,0 @@
|
|||||||
#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);
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#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]);
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
#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));
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user