Compare commits

..

216 Commits

Author SHA1 Message Date
Ratstail91 eaad8d5241 Messing with tests 2023-08-28 23:31:09 +10:00
Ratstail91 308fba1a8c Added carriage return as escapable character 2023-08-28 22:04:21 +10:00
Ratstail91 bcc9df928a Investigating mingw behaviour 2023-08-28 21:59:06 +10:00
Ratstail91 c66e77ec57 Merge branch 'main' of https://github.com/Add00/Toy into Add00-main 2023-08-28 21:30:21 +10:00
Kayne Ruse 9dc9316853 Merge pull request #116 from hiperiondev/main
Correct format
2023-08-29 13:07:22 +10:00
hiperiondev a864a1a226 Correct format 2023-08-28 23:57:46 -03:00
Kayne Ruse c645026620 Merge pull request #115 from hiperiondev/main
Add disassemblre group option
2023-08-29 12:50:56 +10:00
hiperiondev a9ccd65da1 Add disassemblre group option 2023-08-28 23:46:02 -03:00
Add00 113c067c96 updated path error messages 2023-08-27 23:39:32 -04:00
Add00 01f710a4f3 Merge branch 'Ratstail91:main' into main 2023-08-24 17:14:33 -04:00
Add00 eb4c44193c added path method and more tests 2023-08-24 17:09:20 -04:00
Kayne Ruse 0da5201829 Merge pull request #114 from hiperiondev/main
Correct disassembler
2023-08-24 21:43:15 +10:00
Add00 214351abaa view result 2023-08-23 23:19:33 -04:00
hiperiondev 6be29ed8c5 Add implicit fn return 2023-08-23 20:48:06 -03:00
hiperiondev 6341d3337f Correct disassembler 2023-08-23 12:37:28 -03:00
Kayne Ruse d4f952eafc Merge pull request #110 from hiperiondev/main
Add disassembler alternative format
2023-08-23 22:09:33 +10:00
hiperiondev d5bc07d3b3 Add header reference 2023-08-23 08:36:27 -03:00
hiperiondev 5a851f6fbe Rename 2023-08-22 20:35:22 -03:00
hiperiondev d8c6a3ec27 Correct memory leak 2023-08-22 20:21:43 -03:00
hiperiondev b5883e248b Correct format 2023-08-22 19:45:14 -03:00
hiperiondev 52048f2466 Correct literal format 2023-08-22 19:31:06 -03:00
hiperiondev 6b8e95d250 Add disassembler alternative format 2023-08-22 18:23:27 -03:00
Add00 73965bc0e4 input and output can now be closed 2023-08-21 22:47:32 -04:00
Ratstail91 5721edc2d1 Tweaked disassembler pretty printing, because I'm bored 2023-08-22 07:27:36 +10:00
Ratstail91 db52c13613 Removed extra scope around for loop body blocks, resolved #107 2023-08-22 01:11:49 +10:00
Add00 46e274d182 Merge branch 'Ratstail91:main' into main 2023-08-21 10:54:27 -04:00
Ratstail91 7290efe069 Tweaked valgrind test
@add00 you'll want to merge these and test them
2023-08-22 00:43:48 +10:00
Add00 925f13abf5 potential fix for mingw32 issue 2023-08-19 22:10:29 -04:00
Add00 9a56a86ec8 add read/write for bools, and fixed sanitize issue 2023-08-19 21:51:44 -04:00
Add00 3ddda21ff2 Improved error messages, and feedback improvements 2023-08-19 15:41:21 -04:00
Ratstail91 0cf92bdeae Added contributors to the README, removed a binary that was accidentally committed 2023-08-19 06:53:34 +10:00
Add00 65acdd2730 in and out file marked as static 2023-08-15 22:53:16 -04:00
Add00 1603ea1798 mostly fixed sanitization issue 2023-08-14 23:09:13 -04:00
Add00 79f2e231db fix stack overflow issue 2023-08-14 22:22:20 -04:00
Add00 ab0720a5ef memory leak and several bugs fixed 2023-08-14 22:02:33 -04:00
Add00 901940a3d0 Merge branch 'Ratstail91:main' into main 2023-08-14 18:56:13 -04:00
Kayne Ruse 4c9a2e5378 Merge pull request #106 from hiperiondev/main
Disassembler: Some optimizations
2023-08-15 03:57:29 +10:00
hiperiondev 1e11e9eea7 Start independent code from PC zero. Add hierarchical notation for function index. Add guard for end literals. Some presentation formats. 2023-08-14 14:46:07 -03:00
Kayne Ruse cce8ae1ea3 Added disassembler, thanks @hiperiondev, bumped minor version 2023-08-14 23:06:05 +10:00
Kayne Ruse ce54912232 Removed an extra pair of SCOPE_BEGIN and SCOPE_END from function bytecode
This should reduce the memory footprint a bit
2023-08-14 22:17:33 +10:00
Add00 a0acd27be1 partly fixed memory leak issue 2023-08-13 22:27:54 -04:00
Kayne Ruse 23b55fc360 Fixed execFnDecl accidentally modifying the literalCache for a moment, resolved #105 2023-08-14 10:47:10 +10:00
Add00 0c005d0af2 added file operations and additional tests 2023-08-12 16:02:54 -04:00
Add00 38ba2273dd added accessors, write and additional tests 2023-08-10 23:00:26 -04:00
Add00 1b8d8cfba0 Removed debug code 2023-08-09 08:48:40 -04:00
Add00 648a5f84db fixed close function 2023-08-08 23:41:23 -04:00
Add00 e2dda434f8 Fixed memory leak 2023-08-08 19:25:18 -04:00
Add00 5a8e2c0527 Fixed path 2023-08-08 19:05:35 -04:00
Add00 dea5cf6d43 removed HAL 2023-08-08 19:04:27 -04:00
Add00 930115f94c Merge branch 'Ratstail91:main' into main 2023-08-08 13:57:57 -04:00
Kayne Ruse 62fe86f99b Fixed indexing in argument lists, resolved #102 2023-08-09 02:25:07 +10:00
Add00 2fa6b7ee23 added basic read, and an improved HAL 2023-08-06 09:39:06 -04:00
Ratstail91 401de578a5 Short circuitable operators are extremely loose 2023-08-06 04:53:46 +10:00
Kayne Ruse fb4258f9df Fixed broken test 2023-08-06 04:38:55 +10:00
Ratstail91 f885fdaf4c Short circuits are now functioning correctly, resolved #73 2023-08-06 04:28:02 +10:00
Kayne Ruse cfec1b6911 Added int to float coercions to function args and returns, when specified 2023-08-06 02:17:32 +10:00
Add00 43b58eb421 Library rename, added HAL, added open function 2023-08-04 23:13:43 -04:00
Kayne Ruse a63bdaef1c Merge remote-tracking branch 'refs/remotes/origin/main' 2023-08-04 18:43:32 +10:00
Kayne Ruse 3783c94064 Allow trailing commas when writing a compound 2023-08-04 18:43:07 +10:00
Ratstail91 d292b33184 Tweaked types to stop MSVC complaining 2023-08-04 18:24:54 +10:00
Kayne Ruse 64944c24f6 Snipped some duplicate code, resolved #97 2023-08-04 14:52:01 +10:00
Kayne Ruse 604604e8bc Two opcodes weren't being used, resolved #98 2023-08-04 14:51:08 +10:00
Kayne Ruse 67e49b7477 Fixed the way an identifier was handled, resolved #99 2023-08-04 14:45:07 +10:00
Kayne Ruse 967963c9d7 Fixed a spelling mistake 2023-08-03 15:22:06 +10:00
Add00 36b4a494ae io library file creation 2023-08-02 22:19:09 -04:00
Kayne Ruse 9b469e6eb0 Merge pull request #94 from Add00/main
Adding a math library to toy
2023-08-03 01:40:19 +10:00
Add00 f8094fa17e Added hyperbolic and additional comparisons 2023-08-02 11:25:27 -04:00
Add00 8714c56c3e Implemented feedback 2023-08-02 08:39:50 -04:00
Add00 9faaa311e0 Fixed Memory Leak 2023-08-01 17:50:20 -04:00
Add00 f5ba1181c0 Added arc versions of trigonometric functions 2023-08-01 13:41:55 -04:00
Add00 b06b2d9485 test cases and additional functions 2023-08-01 09:04:37 -04:00
Add00 e3e9ca7ece Added math library 2023-07-31 23:31:12 -04:00
Add00 81fe278c96 Added partial cos and sine implementations 2023-07-31 19:02:55 -04:00
Add00 027d093e21 Added math constants for pi and e 2023-07-31 13:56:06 -04:00
Add00 2eaf7fc71a Merge branch 'main' of https://github.com/Add00/Toy 2023-07-31 13:16:01 -04:00
Add00 c43310f316 Code clean up 2023-07-31 13:13:10 -04:00
Add00 6e07c5f2f4 Merge branch 'Ratstail91:main' into main 2023-07-31 13:11:06 -04:00
Ratstail91 7690dce3f6 Fixed casting and grouping 2023-07-31 17:26:07 +10:00
Ratstail91 1ed1993489 Whoops, lets try that again. 2023-07-31 16:55:27 +10:00
Ratstail91 9b5327b83d Merge bug 2023-07-31 16:22:04 +10:00
Ratstail91 10dbe8f8f1 Fixed casting + grouping, resolved #67 2023-07-31 16:20:45 +10:00
Ratstail91 9e4ad7a9a5 Fixed casting + grouping, resolved #67 2023-07-31 16:17:33 +10:00
Add00 5317a12383 Added radian and degree conversion functions. 2023-07-30 23:17:43 -04:00
Ratstail91 35bfa1b9f1 Tweak, these were annoying me 2023-07-31 12:06:04 +10:00
Kayne Ruse 7f692b4cb4 Renamed the about library to toy_version_info library, resovled #81 2023-07-31 11:15:10 +10:00
Kayne Ruse 0cef0abdb5 Adjusted the layout of nativeClamp() and nativeLerp(), thanks Add00! 2023-07-31 04:51:40 +10:00
Kayne Ruse 6ba42b5a9b Merge pull request #93 from Add00/main
Adding clamp and lerp functions to standard library
2023-07-31 04:12:15 +10:00
Add00 3cb62274c9 Fixed merge issue 2023-07-30 13:21:42 -04:00
Add00 60b561d809 Merge branch 'Ratstail91:main' into main 2023-07-30 13:16:57 -04:00
Add00 70b2dcd829 Added clamp and lerp functions to standard library 2023-07-30 13:15:39 -04:00
Ratstail91 0955b3ff38 Tweak to the MSVC build 2023-07-31 02:04:27 +10:00
Kayne Ruse 4137935468 Added sign() and normalize() to standard lib 2023-07-30 17:46:00 +10:00
Kayne Ruse ebeabcb9d4 Reordered some functions for clarity, caught a bug or two 2023-07-28 02:42:57 +10:00
Kayne Ruse 4d33a9473a Added tools/changing-of-the-guard.cpp, resolved #89
A present for @hyperiondev - just run this in the 'includes/' directory.

It's easier than changing things by hand.
2023-07-28 01:09:58 +10:00
Kayne Ruse 61e3cdba82 Fixed a potential linker issue 2023-07-26 08:13:05 +10:00
Ratstail91 3b7d2be87e Discovered and fixed a misplaced macro 2023-07-26 01:44:15 +10:00
Kayne Ruse fa175203c9 Tweaked docs 2023-07-26 01:08:54 +10:00
Kayne Ruse b4a3e9b42b Renamed drive system files, see #88 2023-07-26 00:53:41 +10:00
Kayne Ruse 6347778ead Moved drive system to repl, resolved #88 2023-07-26 00:46:47 +10:00
Kayne Ruse ba98624e82 Fixed C-API function name, resolved #87, thanks @hiperiondev 2023-07-26 00:22:45 +10:00
Ratstail91 24ce965e76 Smoothed out building on Windows 2023-07-23 23:57:48 +10:00
Kayne Ruse 29c130135c Tweak 2023-07-23 18:34:52 +10:00
Kayne Ruse 01eca37560 Comment tweak 2023-07-22 19:14:33 +10:00
Kayne Ruse 8e5ec7d847 Tweaked docs in repl_tools.h and toy.h 2023-07-22 19:11:45 +10:00
Kayne Ruse 9ed6383630 Wrote tests for the drive system, resolved #75 2023-07-21 05:20:59 +10:00
Kayne Ruse 1ec0f63f76 Removed unneeded safties from the libs
I missed removing these before, and I haven't needed these in a long time.
2023-07-21 04:25:36 +10:00
Kayne Ruse d2341ae227 Bumped version number 2023-07-21 03:08:06 +10:00
Kayne Ruse 3782f2aaaa Comment tweak 2023-07-21 02:59:07 +10:00
Kayne Ruse b636ab9e31 Added a new issue template 2023-07-21 02:41:45 +10:00
Kayne Ruse cdfe17ad53 Added mecha-style comment docs to a bunch of headers 2023-07-20 19:44:40 +10:00
Kayne Ruse 3d7d1179c9 Added mecha tool 2023-07-19 18:09:18 +10:00
Kayne Ruse c3c46b4fc6 Refactored parseIdentiferToValue(), removed unneeded safties 2023-07-15 00:34:15 +10:00
Kayne Ruse 0e41b00ef4 Fixed a post-fix increment issue, highlighted in the game engine 2023-06-20 13:54:21 +10:00
Kayne Ruse f6ec6a8c73 The any type is now recognized as a type properly 2023-06-19 23:16:46 +10:00
Kayne Ruse 2157b2f540 Fixed an obscure compiler bug involving assignments and indexing, read more
TOY_OP_INDEX_ASSIGN_INTERMEDIATE was being used when it shouldn't have.

Now the check runs down the whole binary->right branch to ensure the given
node doesn't exist in that tree.
2023-06-15 12:29:25 +10:00
Kayne Ruse 1481216e69 Fixed chained functions, resolved #52 2023-06-14 17:41:30 +10:00
Kayne Ruse f25f389b4e Removed a macro that potentially broke the build
Gonna have to live with repl code in the lib for now.
2023-06-14 16:53:48 +10:00
Kayne Ruse deff784df8 Removed a speed test script 2023-06-14 16:40:01 +10:00
Kayne Ruse 54e82846c3 Massive dict copying optimisation, read more
I simply pre-allocated the new dict to the right size. This skips
internal copying logic which was repeated on every expansion. This
Should increase scope copying as well.

I applied the same logic to arrays, but the increase in speed was tiny.
2023-06-13 14:49:46 +10:00
Kayne Ruse 67fce427eb Added an initial sorted test to the sort() function 2023-06-13 08:17:42 +10:00
Kayne Ruse 8a2cb61435 Made quicksort on mostly-sorted arrays more efficient 2023-06-13 07:28:54 +10:00
Ratstail91 50d03e28fc Fixed MSVC compilation 2023-06-12 00:05:24 +10:00
Kayne Ruse 763581c73b Added header-only parsing to the repl, read more
Also:

* Ensured TOY_VERSION_BUILD is consistent throughout the whole build
* Updated README.md
2023-06-07 23:55:30 +10:00
Kayne Ruse cdb2613e5d Disallowed fn decl in for loop pre clause 2023-06-07 19:20:50 +10:00
Kayne Ruse 733df87c08 Added dist target, lowered recursion depth limit 2023-06-07 14:58:51 +10:00
Kayne Ruse bfd506f497 Forgot memory allocator for reffunctions 2023-06-07 02:02:35 +10:00
Kayne Ruse 18b59c9e84 Bumped version number 2023-06-07 00:11:34 +10:00
Ratstail91 d3eb31d964 Added TOY_DISABLE_REPL option for compiling 2023-06-07 00:04:05 +10:00
Kayne Ruse 07f4a98b95 Replacing Toy_Literal function bytecode with Toy_RefFunction, addressing #77
This seems to have worked way too easily.
2023-06-06 23:35:59 +10:00
Kayne Ruse 0949fd6ff9 Dang 2023-06-06 21:46:42 +10:00
Kayne Ruse 03e5096f10 Moved test_sum into it's own directory under scripts/ 2023-06-06 21:14:05 +10:00
Kayne Ruse bb81b8c474 Changed recursion limit to 10,000 (was 200) 2023-06-06 21:02:01 +10:00
Kayne Ruse cf6db57787 Whitespace tweak 2023-03-25 01:43:51 +11:00
Kayne Ruse 17f0e4476b Caught a bug that the test cases failed to find 2023-03-17 21:58:13 +11:00
Kayne Ruse 1095e1a885 Added type casting a grouping bugfix, resolved #76 2023-03-17 20:57:47 +11:00
Kayne Ruse 2edfbbe3ef Found a compiler bug, thanks Aedan! 2023-03-17 14:01:16 +11:00
Ratstail91 4b83f1f0d6 Fixed a dumb typo 2023-03-15 06:39:19 +11:00
Kayne Ruse e2fa1cf2e8 Moved lib_runner's drive system into the core of the lang 2023-03-15 06:12:35 +11:00
Kayne Ruse a04d2c4816 Tweaked TOY_EXPORT omitting extra repl stuff 2023-03-15 04:56:26 +11:00
Kayne Ruse f2f8aed23a Added short-circuiting support to && and || 2023-03-11 17:59:09 +11:00
Kayne Ruse 68ed52b347 Tweaked precedence of binary expressions 2023-03-11 17:47:43 +11:00
Kayne Ruse 88dac53ae0 Added toy.h, thanks for the suggestion GabrielGavrilov!
Resolved #72
2023-03-10 08:41:58 +11:00
Kayne Ruse f84cdff883 Fixed order of operations 2023-03-07 06:49:17 +11:00
Ratstail91 f869c9425a Corrected an error message 2023-03-05 13:05:16 +11:00
Ratstail91 76ddd5703e Hack: just track the intermediate depth externally 2023-03-05 00:24:07 +11:00
Ratstail91 669808730e Minor tweak that shouldn't break anything 2023-03-04 22:57:41 +11:00
Ratstail91 e6d9809da5 Famous last words: I think I fixed it 2023-03-04 22:18:17 +11:00
Ratstail91 502032e514 Testing an obscure bugfix 2023-03-04 15:41:55 +11:00
Ratstail91 6e9d42f892 Merge branch 'dev' 2023-02-28 17:39:05 +11:00
Ratstail91 70ca27486e Bugfix a leak? 2023-02-28 17:37:43 +11:00
Ratstail91 12fa434e0f Experimenting with cleaning up loopy code 2023-02-28 17:29:37 +11:00
Ratstail91 efc1e764d2 Patched a casting error in round 2023-02-27 23:27:11 +11:00
Kayne Ruse c5c0122243 BUGFIX: typeof keyword precedence was off 2023-02-27 21:47:38 +11:00
Kayne Ruse 348b7b8c24 Added some math utils to standard
* ceil
* floor
* max
* min
* round
2023-02-27 21:32:31 +11:00
Kayne Ruse e243ad949a Removed a divide instruction (modulo) from the final output, thanks Wren! 2023-02-26 22:41:58 +11:00
Ratstail91 9b673f23ad Reduced C callstack size in Toy_Scope 2023-02-26 22:31:37 +11:00
Kayne Ruse 624a0c80ba Prevented NO-OP calls to the memory allocator
Also shaved off about 1-2 milliseconds of execution time of fib-memo.toy
2023-02-26 21:20:22 +11:00
Ratstail91 1064b69d04 BUGFIX: Integer and float comparisons always return true 2023-02-26 01:27:21 +11:00
Ratstail91 e9b347acb6 MSVC + Box Engine are dumber than a bag of rocks 2023-02-25 04:40:12 +11:00
Ratstail91 071c8da2aa Visual Studio broke itself - fixed 2023-02-25 04:28:07 +11:00
Ratstail91 d6538812bf Merge branch 'main' of https://github.com/Ratstail91/Toy 2023-02-25 04:18:03 +11:00
Ratstail91 3aeddff736 Tweaks to dictionary for performance 2023-02-24 22:13:50 +11:00
Ratstail91 c88c1b125d Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-24 21:53:42 +11:00
Kayne Ruse 1513ba9878 tweaked scripts folder 2023-02-23 22:45:38 +11:00
Kayne Ruse bc0289c3f4 tweaked scripts folder 2023-02-23 20:23:10 +11:00
Kayne Ruse 92c71a374d Implemented a basic random library 2023-02-23 19:19:17 +11:00
Kayne Ruse e0547474b8 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-23 18:37:11 +11:00
Kayne Ruse 3e6d21afbb Added abs(), hash() to libstandard 2023-02-23 18:36:12 +11:00
Kayne Ruse d3df01c1c4 Updated .gitignore 2023-02-23 03:33:52 +11:00
Ratstail91 cdca6fa45c Fixed directory in solution file 2023-02-22 20:06:48 +11:00
Kayne Ruse 1dde9d8f29 Improved error message in set() and push()
The actual issue was that the type check wasn't catching the issue, so
it reached the scope before it was caught. Fixed it, anyway.
2023-02-20 13:04:35 +00:00
Kayne Ruse 7f0f17b6e0 Patched up failures from Toy_parseIdentifierToValue
I really don't like that function - it needs to be replaced.
2023-02-20 06:11:30 +00:00
Kayne Ruse 3507104121 Fixed indexAccess potentially going awry with bad inputs
There's always one or two that slip through
2023-02-20 05:28:25 +00:00
Kayne Ruse 87de634e30 Updated version number to 1.0.0 2023-02-20 02:08:42 +00:00
Kayne Ruse 6fa224fa7b Hooks can't be dict keys, tweaked Toy_readFile 2023-02-18 16:47:38 +00:00
Kayne Ruse 8a68d864e6 Opaque type check added 2023-02-18 15:21:49 +00:00
Kayne Ruse 49f240ea07 Minor tweak 2023-02-18 12:15:23 +00:00
Kayne Ruse 3acbd7447a Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-18 11:57:22 +00:00
Kayne Ruse 6f126e6daa Minor tweaks and renames, as I'm documenting 2023-02-18 11:56:18 +00:00
Kayne Ruse 2adb9d9158 Tweaked lib runner API 2023-02-16 22:04:47 +00:00
Kayne Ruse 1668dca255 Tweaked some APIs, hid some functions I don't want in the API 2023-02-16 13:06:07 +00:00
Kayne Ruse 501ff6fff4 Chased a ghost for a few hours 2023-02-14 18:55:24 +00:00
Kayne Ruse 3845627fe5 Added release build to MSVC 2023-02-14 18:03:04 +00:00
Kayne Ruse cdae03bd54 String and identifier making fixed for MSVC, just in case 2023-02-14 17:38:10 +00:00
Kayne Ruse 7b501b71b5 commandLine now initializes with default values 2023-02-14 17:00:16 +00:00
Kayne Ruse 913738a4d1 Tweaked the runner test, should be orders of magnitude faster 2023-02-14 16:16:48 +00:00
Kayne Ruse 3312a38c7c Updated memusage tool 2023-02-14 16:05:43 +00:00
Kayne Ruse 71b57fd42c Fixed scripts for distribution 2023-02-14 10:35:08 +00:00
Kayne Ruse 453afbab41 Fixed a stupid bug in MSVC 2023-02-14 10:24:43 +00:00
Kayne Ruse 57af5a6d59 Tweaked some scripts 2023-02-14 09:21:22 +00:00
Kayne Ruse 0737b2a483 Dropped underscore functions in favour of UFCS 2023-02-14 08:37:31 +00:00
Kayne Ruse eae96d6403 Corrected the order of arguments to Toy_callLiteralFn() 2023-02-14 08:00:35 +00:00
Kayne Ruse b55b8e879e Added -n option to diable print newline 2023-02-13 15:51:38 +00:00
Kayne Ruse 1ed114b80d Allow for stmt to have empty clauses, resolved #58 2023-02-13 14:45:24 +00:00
Kayne Ruse eb8e522bf2 Merged standard and timer, resolved #48 2023-02-13 13:58:41 +00:00
Kayne Ruse 16b71ba6f4 Implemented quicksort in _sort() 2023-02-13 13:31:58 +00:00
Kayne Ruse 9725f3c6a3 Patched some very obscure bugs 2023-02-12 16:54:44 +00:00
Kayne Ruse 8653a2663f Added _indexOf 2023-02-12 14:32:26 +00:00
Kayne Ruse ab2cd5dc93 Removed lib timer properly, see #62 2023-02-12 14:19:14 +00:00
Kayne Ruse 724804a78a Playing with level.toy 2023-02-11 15:27:23 +00:00
Kayne Ruse 77a128e0f7 Added the -t option to the repl 2023-02-11 14:51:19 +00:00
Kayne Ruse 5343e1054d Straightened out file extensions 2023-02-11 14:26:55 +00:00
Kayne Ruse 3930ec0477 Tweaked README.md 2023-02-11 06:48:16 +00:00
Kayne Ruse 996744d7ec Resolved #59 2023-02-11 05:10:32 +00:00
Kayne Ruse c00b32017b Dummied out lib timer 2023-02-11 01:42:44 +00:00
Kayne Ruse 457014d577 Added MSVC build support, likely broke tests 2023-02-11 00:49:21 +00:00
Kayne Ruse be4cbf1ad6 Pack 'em up! 2023-02-10 21:53:38 +00:00
Kayne Ruse aeb008c684 Fixed unary negation bug, removed newline from print 2023-02-10 18:38:25 +00:00
Kayne Ruse 53012dbce1 Added _filter() 2023-02-10 15:41:38 +00:00
Kayne Ruse 4fe57f9562 Added _containsKey() and _containsValue() 2023-02-10 15:27:39 +00:00
Kayne Ruse 3ba2e420ea Added _every() and _some() 2023-02-10 15:00:15 +00:00
Kayne Ruse c81a139c97 Now handles unterminated block comments without freezing 2023-02-10 12:26:38 +00:00
Kayne Ruse 66ea684a90 Disabled comments in the repl 2023-02-10 12:11:42 +00:00
Kayne Ruse a26a6a56d0 Patched a pre/postfix increment/decrement segfault 2023-02-10 11:49:59 +00:00
Kayne Ruse ee226ea426 Strengthened constness for cstrings and bytecode 2023-02-10 08:52:38 +00:00
Kayne Ruse 76a0290290 Removed export keyword from README.md 2023-02-09 17:46:28 +00:00
132 changed files with 12061 additions and 3598 deletions
+10
View File
@@ -0,0 +1,10 @@
---
name: Question
about: Ask a Question
labels: question
---
### How can I help?
I'm always here to help with any inquiries you have regarding Toy and its related projects.
+1 -1
View File
@@ -2,7 +2,7 @@ name: Comprehensive Tests
on:
push:
branches: [ "main", "dev" ]
branches: [ "main", "*" ]
pull_request:
branches: [ "main" ]
+56 -28
View File
@@ -1,31 +1,59 @@
#Editor generated files
*.sln
*.vcproj
*.suo
*.ncb
*.user
compile_commands.json
# Prerequisites
*.d
#Directories
Release/
Debug/
Out/
release/
debug/
out/
.cache/
#Project generated files
*.db
# Object files
*.o
*.a
*.exe
*.meta
*.log
out
*.stackdump
*.tb
*.ko
*.obj
*.elf
#Shell files
*.bat
*.sh
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
.cproject
.project
.settings/
temp/
Release/
out/
+19 -7
View File
@@ -4,9 +4,11 @@
# Toy
This is the Toy programming language interpreter, written in C.
The Toy programming language is an imperative bytecode-intermediate embedded scripting language. It isn't intended to operate on its own, but rather as part of another program, the "host". This process is intended to allow a decent amount of easy customisation by the host's end user, by exposing logic in script files. Alternatively, binary files in a custom format can be used as well.
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
The host will provide all of the extensions needed on a case-by-case basis. Script files have the `.toy` file extension, while binary files have the `.tb` file extension.
This is the Toy programming language interpreter, written in C.
# Nifty Features
@@ -14,7 +16,7 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
* Bytecode intermediate compilation
* Optional, but robust type system (including `opaque` for arbitrary data)
* Functions and types are first-class citizens
* Import external libraries
* Import native libraries from the host
* Fancy slice notation for strings, arrays and dictionaries
* Can re-direct output, error and assertion failure messages
* Open source under the zlib license
@@ -23,7 +25,9 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
For Windows(MSVC), Visual Studio project files are included.
Note: MacOS and Windows(MSVC) are not officially supported, but we'll do our best!
## Tools
@@ -31,12 +35,13 @@ 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
@@ -60,15 +65,22 @@ var tally = makeCounter();
print tally(); //1
print tally(); //2
print tally(); //3
export tally; //export this variable to the host program
```
# License
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
# Contributions
@hiperiondev - Disassembler, porting support and feedback
@add00 - Library support
@gruelingpine185 - Unofficial MacOS support
@solar-mist - Minor bugfixes
Unnamed Individuals - Feedback
# Patrons via Patreon
* Seth A. Robinson
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
+159
View File
@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{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
View File
@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{26360002-CC2A-469A-9B28-BA0C1AF41657}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)out\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)out\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TOY_EXPORTS;TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;TOY_EXPORTS;TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Link>
<OutputFile>$(Outdir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Bscmake>
<OutputFile>$(Platform)\$(Configuration)\$(TargetName).bsc</OutputFile>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<PreprocessorDefinitions>TOY_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Link>
<OutputFile>$(Outdir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Bscmake>
<OutputFile>$(Platform)\$(Configuration)\$(TargetName).bsc</OutputFile>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="source\toy_ast_node.c" />
<ClCompile Include="source\toy_builtin.c" />
<ClCompile Include="source\toy_common.c" />
<ClCompile Include="source\toy_compiler.c" />
<ClCompile Include="source\toy_interpreter.c" />
<ClCompile Include="source\toy_keyword_types.c" />
<ClCompile Include="source\toy_lexer.c" />
<ClCompile Include="source\toy_literal.c" />
<ClCompile Include="source\toy_literal_array.c" />
<ClCompile Include="source\toy_literal_dictionary.c" />
<ClCompile Include="source\toy_memory.c" />
<ClCompile Include="source\toy_parser.c" />
<ClCompile Include="source\toy_reffunction.c" />
<ClCompile Include="source\toy_refstring.c" />
<ClCompile Include="source\toy_scope.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\toy.h" />
<ClInclude Include="source\toy_ast_node.h" />
<ClInclude Include="source\toy_builtin.h" />
<ClInclude Include="source\toy_common.h" />
<ClInclude Include="source\toy_compiler.h" />
<ClInclude Include="source\toy_console_colors.h" />
<ClInclude Include="source\toy_interpreter.h" />
<ClInclude Include="source\toy_keyword_types.h" />
<ClInclude Include="source\toy_lexer.h" />
<ClInclude Include="source\toy_literal.h" />
<ClInclude Include="source\toy_literal_array.h" />
<ClInclude Include="source\toy_literal_dictionary.h" />
<ClInclude Include="source\toy_memory.h" />
<ClInclude Include="source\toy_opcodes.h" />
<ClInclude Include="source\toy_parser.h" />
<ClInclude Include="source\toy_reffunction.h" />
<ClInclude Include="source\toy_refstring.h" />
<ClInclude Include="source\toy_scope.h" />
<ClInclude Include="source\toy_token_types.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+44
View File
@@ -0,0 +1,44 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Toy", "Toy.vcxproj", "{26360002-CC2A-469A-9B28-BA0C1AF41657}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Repl", "Repl.vcxproj", "{97F823E5-3AB8-47EF-B142-C15DD7CADF76}"
ProjectSection(ProjectDependencies) = postProject
{26360002-CC2A-469A-9B28-BA0C1AF41657} = {26360002-CC2A-469A-9B28-BA0C1AF41657}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.ActiveCfg = Debug|x64
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x64.Build.0 = Debug|x64
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.ActiveCfg = Debug|Win32
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Debug|x86.Build.0 = Debug|Win32
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.ActiveCfg = Release|x64
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x64.Build.0 = Release|x64
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.ActiveCfg = Release|Win32
{26360002-CC2A-469A-9B28-BA0C1AF41657}.Release|x86.Build.0 = Release|Win32
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.ActiveCfg = Debug|x64
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x64.Build.0 = Debug|x64
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.ActiveCfg = Debug|Win32
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Debug|x86.Build.0 = Debug|Win32
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.ActiveCfg = Release|x64
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x64.Build.0 = Release|x64
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.ActiveCfg = Release|Win32
{97F823E5-3AB8-47EF-B142-C15DD7CADF76}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7089F1AD-8EC0-4F27-AFD1-5FD43D91AABC}
EndGlobalSection
EndGlobal
+27 -8
View File
@@ -1,7 +1,3 @@
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
# export CFLAGS+=-fsanitize=address,undefined
export CFLAGS+=-std=c18 -pedantic -Werror
export TOY_OUTDIR = out
@@ -10,10 +6,10 @@ all: $(TOY_OUTDIR) repl
#repl builds
repl: $(TOY_OUTDIR) library
$(MAKE) -C repl
$(MAKE) -j8 -C repl
repl-static: $(TOY_OUTDIR) static
$(MAKE) -C repl
$(MAKE) -j8 -C repl
repl-release: clean $(TOY_OUTDIR) library-release
$(MAKE) -C repl release
@@ -28,12 +24,16 @@ library: $(TOY_OUTDIR)
static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static
library-release: $(TOY_OUTDIR)
library-release: clean $(TOY_OUTDIR)
$(MAKE) -j8 -C source library-release
static-release: $(TOY_OUTDIR)
static-release: clean $(TOY_OUTDIR)
$(MAKE) -j8 -C source static-release
#distribution
dist: export CFLAGS+=-O2 -mtune=native -march=native
dist: repl-release
#utils
test: clean $(TOY_OUTDIR)
$(MAKE) -C test
@@ -51,6 +51,25 @@ $(TOY_OUTDIR):
install-tools:
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
#utils
build-mecha: $(TOY_OUTDIR)
g++ -o $(TOY_OUTDIR)/mecha tools/mecha.cpp
build-docs: build-mecha
$(TOY_OUTDIR)/mecha $(wildcard source/*.h)
$(TOY_OUTDIR)/mecha $(wildcard repl/*.h)
docs:
mkdir docs
move-docs: docs
mv -u $(wildcard source/*.md) docs
mv -u $(wildcard repl/*.md) docs
documentation:
$(MAKE) build-docs
$(MAKE) move-docs
.PHONY: clean
clean:
+99
View File
@@ -0,0 +1,99 @@
#include "drive_system.h"
#include "toy_memory.h"
#include "toy_literal_dictionary.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//file system API
static Toy_LiteralDictionary driveDictionary;
void Toy_initDriveSystem() {
Toy_initLiteralDictionary(&driveDictionary);
}
void Toy_freeDriveSystem() {
Toy_freeLiteralDictionary(&driveDictionary);
}
void Toy_setDrivePath(char* drive, char* path) {
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(drive));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(path));
Toy_setLiteralDictionary(&driveDictionary, driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
}
Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getDrivePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getDrivePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* filePath = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal pathLiteral = Toy_getLiteralDictionary(&driveDictionary, driveLiteral);
if (!TOY_IS_STRING(pathLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(pathLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
Toy_deleteRefString(filePath);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* path = Toy_copyRefString(TOY_AS_STRING(pathLiteral));
size_t fileLength = Toy_lengthRefString(path) + Toy_lengthRefString(filePath);
char* file = TOY_ALLOCATE(char, fileLength + 1); //+1 for null
snprintf(file, fileLength, "%s%s", Toy_toCString(path), Toy_toCString(filePath));
//clean up the drive/path stuff
Toy_deleteRefString(drivePath);
Toy_deleteRefString(filePath);
Toy_deleteRefString(path);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//check for break-out attempts
for (size_t i = 0; i < fileLength - 1; i++) {
if (file[i] == '.' && file[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, file, fileLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(file, fileLength));
TOY_FREE_ARRAY(char, file, fileLength + 1);
return result;
}
+76
View File
@@ -0,0 +1,76 @@
#pragma once
/*!
# drive_system.h
When accessing the file system through Toy (such as with the runner library), it's best practice to utilize the drive system - this system (tries to) prevent malicious accessing of files outside of the designated folders. It does this by causing an error when a script tries to access a parent directory.
To use the drive system, first you must designate specific folders which can be accessed, like so:
```c
#include "drive_system.h"
int main(int argc, char* argv[]) {
//the drive system uses a LiteralDictionary, which must be initialized with this
Toy_initDriveSystem();
Toy_setDrivePath("scripts", "assets/scripts");
Toy_setDrivePath("sprites", "assets/sprites");
Toy_setDrivePath("fonts", "assets/fonts");
//TODO: do you stuff here
//clean up the drive dictionary when you're done
Toy_freeDriveSystem();
return 0;
}
```
This utility is intended mainly for libraries to use - as such, the core of Toy does not utilize it.
### Implementation Details
The drive system uses a Toy's Dictionary structure to store the mappings between keys and values - this dictionary object is a static global which persists for the lifetime of the program.
!*/
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_interpreter.h"
/*!
## Defined Functions
!*/
/*!
### void Toy_initDriveSystem()
This function initializes the drive system.
!*/
TOY_API void Toy_initDriveSystem();
/*!
### void Toy_freeDriveSystem()
This function cleans up after the drive system is no longer needed.
!*/
TOY_API void Toy_freeDriveSystem();
/*!
### void Toy_setDrivePath(char* drive, char* path)
This function sets a key-value pair in the drive system. It uses C strings, since its intended to be called directly from `main()`.
!*/
TOY_API void Toy_setDrivePath(char* drive, char* path);
/*!
### Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral)
This function, when given a string literal of the correct format, will return a new string literal containing the relative filepath to a specified file.
The correct format is `drive:/path/to/filename`, where `drive` is a drive that was specified with `Toy_setDrivePath()`.
On failure, this function returns a null literal.
!*/
TOY_API Toy_Literal Toy_getDrivePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
-1003
View File
File diff suppressed because it is too large Load Diff
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+940
View File
@@ -0,0 +1,940 @@
#include "lib_fileio.h"
#include "toy_memory.h"
#include "drive_system.h"
#include <limits.h>
#include <stdio.h>
typedef struct Toy_File
{
FILE* fp;
Toy_RefString* mode;
Toy_RefString* path;
} Toy_File;
Toy_File* createToyFile(Toy_RefString* mode, Toy_RefString* path) {
Toy_File* file = TOY_ALLOCATE(Toy_File, 1);
file->fp = NULL;
file->mode = Toy_copyRefString(mode);
file->path = Toy_copyRefString(path);
return file;
}
void deleteToyFile(Toy_File* file) {
Toy_deleteRefString(file->mode);
Toy_deleteRefString(file->path);
TOY_FREE(Toy_File, file);
}
static int nativeOpen(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count < 1) {
interpreter->errorOutput("Too few arguments open(string, string) expects two arguments\n");
return -1;
}
else if (arguments->count > 2) {
interpreter->errorOutput("Too many arguments open(string, string) expects two arguments\n");
return -1;
}
Toy_Literal modeLiteral = arguments->count == 2? Toy_popLiteralArray(arguments) : TOY_TO_STRING_LITERAL(Toy_createRefString("r"));
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
// parse the drivePath (if it's an identifier)
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
Toy_freeLiteral(drivePathLiteralIdn);
}
// check the drivePath type
if (!TOY_IS_STRING(drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type expected a string as the first argument to open(string, string)\n");
Toy_freeLiteral(drivePathLiteral);
Toy_freeLiteral(modeLiteral);
return -1;
}
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
interpreter->errorOutput("File not found in the specified drive\n");
Toy_freeLiteral(drivePathLiteral);
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(modeLiteral);
return -1;
}
// parse the mode (if it's an identifier)
Toy_Literal modeLiteralIdn = modeLiteral;
if (TOY_IS_IDENTIFIER(modeLiteral) && Toy_parseIdentifierToValue(interpreter, &modeLiteral)) {
Toy_freeLiteral(modeLiteralIdn);
}
// check the mode type
if (!TOY_IS_STRING(modeLiteral)) {
interpreter->errorOutput("Incorrect argument type expected a string as the second argument to open(string, string)\n");
Toy_freeLiteral(drivePathLiteral);
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(modeLiteral);
return -1;
}
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
size_t filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
const char* mode = Toy_toCString(TOY_AS_STRING(modeLiteral));
// build file object
Toy_File* file = createToyFile(TOY_AS_STRING(modeLiteral), TOY_AS_STRING(filePathLiteral));
// attempt to open file
file->fp = fopen(filePath, mode);
// result
Toy_Literal fileLiteral = TOY_TO_NULL_LITERAL;
if (file->fp == NULL) {
deleteToyFile(file);
}
else {
fileLiteral = TOY_TO_OPAQUE_LITERAL(file, TOY_OPAQUE_TAG_FILE);
}
Toy_pushLiteralArray(&interpreter->stack, fileLiteral);
// cleanup
Toy_freeLiteral(fileLiteral);
Toy_freeLiteral(drivePathLiteral);
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(modeLiteral);
return 1;
}
static int nativeClose(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments close() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type close() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
int result = 0;
if (
file->fp != stdout &&
file->fp != stdin &&
file->fp != NULL
) {
result = fclose(file->fp);
file->fp = NULL;
}
// return the result
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != EOF);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
deleteToyFile(file);
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeRead(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count < 2) {
interpreter->errorOutput("Too few arguments read(type) expects one argument\n");
return -1;
}
else if (arguments->count > 2) {
interpreter->errorOutput("Too many arguments read(type) expects one argument\n");
return -1;
}
Toy_Literal typeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the type (if it's an identifier)
Toy_Literal typeLiteralIdn = typeLiteral;
if (TOY_IS_IDENTIFIER(typeLiteral) && Toy_parseIdentifierToValue(interpreter, &typeLiteral)) {
Toy_freeLiteral(typeLiteralIdn);
}
// check the type type
if (!TOY_IS_TYPE(typeLiteral)) {
interpreter->errorOutput("Incorrect argument type expected a type as the first argument to read(type)\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(typeLiteral);
return -1;
}
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type, read(type) expects a file type\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(typeLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
int error = 0;
switch (TOY_AS_TYPE(typeLiteral).typeOf) {
case TOY_LITERAL_BOOLEAN: {
char value = '0';
error = fscanf(file->fp, "%c", &value);
resultLiteral = TOY_TO_BOOLEAN_LITERAL(value != '0');
break;
}
case TOY_LITERAL_INTEGER: {
int value = 0;
error = fscanf(file->fp, "%i", &value);
resultLiteral = TOY_TO_INTEGER_LITERAL(value);
break;
}
case TOY_LITERAL_FLOAT: {
float value = 0.0f;
error = fscanf(file->fp, "%f", &value);
resultLiteral = TOY_TO_FLOAT_LITERAL(value);
break;
}
case TOY_LITERAL_STRING: { //BUG: needs a terminator to show how much to read
char value[TOY_MAX_STRING_LENGTH] = {0};
size_t size = fread(value, sizeof(char), TOY_MAX_STRING_LENGTH - 1, file->fp);
value[size] = '\0';
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(value));
break;
}
default: {
// TODO handle other types
break;
}
}
if (error != EOF) {
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
}
else {
Toy_pushLiteralArray(&interpreter->stack, TOY_TO_NULL_LITERAL);
}
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeWrite(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count < 2) {
interpreter->errorOutput("Too few arguments write(any) expects one argument\n");
return -1;
}
else if (arguments->count > 2) {
interpreter->errorOutput("Too many arguments write(any) expects one argument\n");
return -1;
}
Toy_Literal valueLiteral = Toy_popLiteralArray(arguments);
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the value (if it's an identifier)
Toy_Literal valueLiteralIdn = valueLiteral;
if (TOY_IS_IDENTIFIER(valueLiteral) && Toy_parseIdentifierToValue(interpreter, &valueLiteral)) {
Toy_freeLiteral(valueLiteralIdn);
}
// check the value type
if (TOY_IS_NULL(valueLiteral)) {
interpreter->errorOutput("Incorrect argument type expected non null value as the first argument to write(any)\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type write(any) expects a file type\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
int result = 0;
switch (valueLiteral.type) {
case TOY_LITERAL_BOOLEAN: {
result = fprintf(file->fp, "%i", TOY_AS_BOOLEAN(valueLiteral));
break;
}
case TOY_LITERAL_INTEGER: {
result = fprintf(file->fp, "%i", TOY_AS_INTEGER(valueLiteral));
break;
}
case TOY_LITERAL_FLOAT: {
result = fprintf(file->fp, "%f", TOY_AS_FLOAT(valueLiteral));
break;
}
case TOY_LITERAL_STRING: {
result = fprintf(file->fp, "%s", Toy_toCString(TOY_AS_STRING(valueLiteral)));
break;
}
default: {
// TODO handle other types
break;
}
}
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result > 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(valueLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeRename(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count < 2) {
interpreter->errorOutput("Too few arguments rename(string) expects one argument\n");
return -1;
}
else if (arguments->count > 2) {
interpreter->errorOutput("Too many arguments rename(string) expects one argument\n");
return -1;
}
Toy_Literal valueLiteral = Toy_popLiteralArray(arguments);
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the value (if it's an identifier)
Toy_Literal valueLiteralIdn = valueLiteral;
if (TOY_IS_IDENTIFIER(valueLiteral) && Toy_parseIdentifierToValue(interpreter, &valueLiteral)) {
Toy_freeLiteral(valueLiteralIdn);
}
// check the value type
if (!TOY_IS_STRING(valueLiteral)) {
interpreter->errorOutput("Incorrect argument type expected a string as the first argument to rename(string)\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type, rename(string) expects a file type\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(valueLiteral);
return -1;
}
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &valueLiteral);
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
const char* newName = Toy_toCString(TOY_AS_STRING(filePathLiteral));
// close the file
if (file->fp != NULL) {
fclose(file->fp);
file->fp = NULL;
}
// rename the file
int result = rename(Toy_toCString(file->path), newName);
// open file again
file->fp = fopen(newName, Toy_toCString(file->mode));
// update the file object's name
Toy_deleteRefString(file->path);
file->path = Toy_createRefString(newName);
// return result
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result == 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(valueLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeSeek(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count < 3) {
interpreter->errorOutput("Too few arguments seek(string, int) expects two arguments\n");
return -1;
}
else if (arguments->count > 3) {
interpreter->errorOutput("Too many arguments seek(string, int) expects two arguments\n");
return -1;
}
Toy_Literal offsetLiteral = Toy_popLiteralArray(arguments);
Toy_Literal originLiteral = Toy_popLiteralArray(arguments);
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the origin (if it's an identifier)
Toy_Literal originLiteralIdn = originLiteral;
if (TOY_IS_IDENTIFIER(originLiteral) && Toy_parseIdentifierToValue(interpreter, &originLiteral)) {
Toy_freeLiteral(originLiteralIdn);
}
// check the origin type
if (!TOY_IS_STRING(originLiteral)) {
interpreter->errorOutput("Incorrect argument type expected a string as the first argument to seek(string, int)\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(offsetLiteral);
Toy_freeLiteral(originLiteral);
return -1;
}
// parse the offset (if it's an identifier)
Toy_Literal offsetLiteralIdn = offsetLiteral;
if (TOY_IS_IDENTIFIER(offsetLiteral) && Toy_parseIdentifierToValue(interpreter, &offsetLiteral)) {
Toy_freeLiteral(offsetLiteralIdn);
}
// check the offset type
if (!TOY_IS_INTEGER(offsetLiteral)) {
interpreter->errorOutput("Incorrect argument type expected a int as the second argument to seek(string, int)\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(offsetLiteral);
Toy_freeLiteral(originLiteral);
return -1;
}
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type seek(string, int) expects a file type\n");
Toy_freeLiteral(selfLiteral);
Toy_freeLiteral(offsetLiteral);
Toy_freeLiteral(originLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
Toy_RefString* orginString = TOY_AS_STRING(originLiteral);
int offset = TOY_AS_INTEGER(offsetLiteral);
int origin = -1;
if (Toy_equalsRefStringCString(orginString, "bgn")) {
origin = SEEK_SET;
}
else if (Toy_equalsRefStringCString(orginString, "cur")) {
origin = SEEK_CUR;
}
else if (Toy_equalsRefStringCString(orginString, "end")) {
origin = SEEK_END;
}
int result = origin >= SEEK_SET && origin <= SEEK_END?
fseek(file->fp, offset, origin) : -1;
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result == 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(originLiteral);
Toy_freeLiteral(offsetLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeError(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments error() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type error() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
int result = ferror(file->fp);
// return the result
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeCompleted(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments completed() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type completed() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
int result = feof(file->fp);
// return the result
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativePosition(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments position() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type position() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
// pervent integer overflow as ftell returns a long
int size = ftell(file->fp) > INT_MAX? INT_MAX : ftell(file->fp);
// return the result
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(size);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeSize(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments size() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type size() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
int size = 0;
fseek(file->fp, 0, SEEK_END);
// pervent integer overflow as ftell returns a long
if (ftell(file->fp) > INT_MAX) {
size = INT_MAX;
}
else {
size = ftell(file->fp);
}
fseek(file->fp, 0, SEEK_SET);
// return the result
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(size);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativeMode(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments mode() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type mode() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
// return the result
Toy_Literal resultLiteral = TOY_TO_STRING_LITERAL(Toy_copyRefString(file->mode));
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
static int nativePath(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
if (arguments->count != 1) {
interpreter->errorOutput("Too many arguments path() expects zero arguments\n");
return -1;
}
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
// parse the self (if it's an identifier)
Toy_Literal selfLiteralIdn = selfLiteral;
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
Toy_freeLiteral(selfLiteralIdn);
}
// check self type
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != TOY_OPAQUE_TAG_FILE) {
interpreter->errorOutput("Incorrect self type path() expects a file type\n");
Toy_freeLiteral(selfLiteral);
return -1;
}
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
// return the result
Toy_Literal resultLiteral = TOY_TO_STRING_LITERAL(Toy_copyRefString(file->path));
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
// cleanup
Toy_freeLiteral(resultLiteral);
Toy_freeLiteral(selfLiteral);
return 1;
}
// call the hook
typedef struct Natives {
char* name;
Toy_NativeFn fn;
} Natives;
typedef struct Variable {
Toy_Literal key;
Toy_Literal identifier;
Toy_Literal literal;
} Variable;
// Helper function create a int variable
void createToyVariableInt(Variable* variable, char* key, int literal) {
variable->key = TOY_TO_STRING_LITERAL(Toy_createRefString(key));
variable->identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(key));
variable->literal = TOY_TO_INTEGER_LITERAL(literal);
}
// Helper function create a file variable
void createToyVariableFile(Variable* variable, char* key, Toy_File* literal) {
variable->key = TOY_TO_STRING_LITERAL(Toy_createRefString(key));
variable->identifier = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString(key));
variable->literal = TOY_TO_OPAQUE_LITERAL(literal, TOY_OPAQUE_TAG_FILE);
}
// Helper function to clean up variables
void deleteToyVariables(Variable variables[], int size) {
for (int i = 0; i < size; i++) {
Toy_freeLiteral(variables[i].key);
Toy_freeLiteral(variables[i].identifier);
Toy_freeLiteral(variables[i].literal);
}
}
// Helper to check for naming conflicts with existing variables
bool scopeConflict(Toy_Interpreter* interpreter, Variable variables[], int size) {
for (int i = 0; i < size; i++) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, variables[i].literal)) {
interpreter->errorOutput("Can't override an existing variable\n");
deleteToyVariables(variables, size);
return true;
}
}
return false;
}
// Helper to place variables into scope should be called after scopeConflict
void exposeVariablesToScope(Toy_Interpreter* interpreter, Variable variables[], int size) {
Toy_Literal intType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_INTEGER, false);
Toy_Literal opaqueType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_OPAQUE, false);
for (int i = 0; i < size; i++) {
if (TOY_IS_INTEGER(variables[i].literal)) {
Toy_declareScopeVariable(interpreter->scope, variables[i].identifier, intType);
}
else if (TOY_IS_OPAQUE(variables[i].literal)) {
Toy_declareScopeVariable(interpreter->scope, variables[i].identifier, opaqueType);
}
Toy_setScopeVariable(interpreter->scope, variables[i].identifier, variables[i].literal, true);
}
Toy_freeLiteral(intType);
Toy_freeLiteral(opaqueType);
}
int Toy_hookFileIO(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
// build the natives list
Natives natives[] = {
// access
{"open", nativeOpen},
{"close", nativeClose},
// operations
{"read", nativeRead},
{"write", nativeWrite},
{"rename", nativeRename},
{"seek", nativeSeek},
// accessors
{"error", nativeError},
{"completed", nativeCompleted},
{"position", nativePosition},
{"size", nativeSize},
{"mode", nativeMode},
{"path", nativePath},
{NULL, NULL}
};
// global variables
const int VARIABLES_SIZE = 5;
Variable variables[VARIABLES_SIZE];
createToyVariableInt(&variables[0], "MAX_FILENAME_SIZE", FILENAME_MAX);
createToyVariableInt(&variables[1], "MAX_FILES_OPEN", FOPEN_MAX);
createToyVariableInt(&variables[2], "END_OF_FILE", EOF);
Toy_RefString* outMode = Toy_createRefString("w");
Toy_RefString* outName = Toy_createRefString("output");
static Toy_File* outFile;
outFile = createToyFile(outMode, outName);
outFile->fp = stdout;
createToyVariableFile(&variables[3], "output", outFile);
Toy_deleteRefString(outMode);
Toy_deleteRefString(outName);
Toy_RefString* inMode = Toy_createRefString("r");
Toy_RefString* inName = Toy_createRefString("input");
static Toy_File* inFile;
inFile = createToyFile(inMode, inName);
inFile->fp = stdin;
createToyVariableFile(&variables[4], "input", inFile);
Toy_deleteRefString(inMode);
Toy_deleteRefString(inName);
// 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);
}
// set global variables
for (int i = 0; i < VARIABLES_SIZE; i++) {
Toy_setLiteralDictionary(dictionary, variables[i].key, variables[i].literal);
}
// build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal anyType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, anyType);
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);
}
if (scopeConflict(interpreter, variables, VARIABLES_SIZE)) {
return -1;
}
exposeVariablesToScope(interpreter, variables, VARIABLES_SIZE);
deleteToyVariables(variables, VARIABLES_SIZE);
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "toy_interpreter.h"
#define TOY_OPAQUE_TAG_FILE 300
int Toy_hookFileIO(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+1152
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookMath(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+181
View File
@@ -0,0 +1,181 @@
#include "lib_random.h"
#include "toy_memory.h"
static int hashInt(int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
typedef struct Toy_RandomGenerator {
int seed; //mutated with each call
} Toy_RandomGenerator;
//Toy native functions
static int nativeCreateRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to createRandomGenerator\n");
return -1;
}
//get the seed argument
Toy_Literal seedLiteral = Toy_popLiteralArray(arguments);
Toy_Literal seedLiteralIdn = seedLiteral;
if (TOY_IS_IDENTIFIER(seedLiteral) && Toy_parseIdentifierToValue(interpreter, &seedLiteral)) {
Toy_freeLiteral(seedLiteralIdn);
}
if (!TOY_IS_INTEGER(seedLiteral)) {
interpreter->errorOutput("Incorrect literal type passed to createRandomGenerator");
Toy_freeLiteral(seedLiteral);
return -1;
}
//generate the generator object
Toy_RandomGenerator* generator = TOY_ALLOCATE(Toy_RandomGenerator, 1);
generator->seed = TOY_AS_INTEGER(seedLiteral);
Toy_Literal generatorLiteral = TOY_TO_OPAQUE_LITERAL(generator, TOY_OPAQUE_TAG_RANDOM);
//return and cleanup
Toy_pushLiteralArray(&interpreter->stack, generatorLiteral);
Toy_freeLiteral(seedLiteral);
Toy_freeLiteral(generatorLiteral);
return 1;
}
static int nativeGenerateRandomNumber(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to generateRandomNumber\n");
return -1;
}
//get the runner object
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
Toy_Literal generatorLiteralIdn = generatorLiteral;
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
Toy_freeLiteral(generatorLiteralIdn);
}
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
interpreter->errorOutput("Unrecognized opaque literal in generateRandomNumber\n");
return -1;
}
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
//generate the new value and package up the return
generator->seed = hashInt(generator->seed);
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(generator->seed);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
Toy_freeLiteral(generatorLiteral);
Toy_freeLiteral(resultLiteral);
return 0;
}
static int nativeFreeRandomGenerator(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to freeRandomGenerator\n");
return -1;
}
//get the runner object
Toy_Literal generatorLiteral = Toy_popLiteralArray(arguments);
Toy_Literal generatorLiteralIdn = generatorLiteral;
if (TOY_IS_IDENTIFIER(generatorLiteral) && Toy_parseIdentifierToValue(interpreter, &generatorLiteral)) {
Toy_freeLiteral(generatorLiteralIdn);
}
if (TOY_GET_OPAQUE_TAG(generatorLiteral) != TOY_OPAQUE_TAG_RANDOM) {
interpreter->errorOutput("Unrecognized opaque literal in freeRandomGenerator\n");
return -1;
}
Toy_RandomGenerator* generator = TOY_AS_OPAQUE(generatorLiteral);
//clear out the runner object
TOY_FREE(Toy_RandomGenerator, generator);
Toy_freeLiteral(generatorLiteral);
return 0;
}
//call the hook
typedef struct Natives {
const char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"createRandomGenerator", nativeCreateRandomGenerator},
{"generateRandomNumber", nativeGenerateRandomNumber},
{"freeRandomGenerator", nativeFreeRandomGenerator},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "toy_interpreter.h"
#define TOY_OPAQUE_TAG_RANDOM 200
int Toy_hookRandom(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+36 -171
View File
@@ -4,13 +4,13 @@
#include "toy_interpreter.h"
#include "repl_tools.h"
#include "drive_system.h"
#include <stdio.h>
#include <stdlib.h>
typedef struct Toy_Runner {
Toy_Interpreter interpreter;
unsigned char* bytecode;
const unsigned char* bytecode;
size_t size;
bool dirty;
@@ -32,7 +32,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
Toy_freeLiteral(drivePathLiteralIdn);
}
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral);
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
@@ -43,12 +43,12 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
Toy_freeLiteral(drivePathLiteral);
//use raw types - easier
char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
int filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
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;
char* source = Toy_readFile(filePath, &fileSize);
const char* source = (const char*)Toy_readFile(filePath, &fileSize);
if (!source) {
interpreter->errorOutput("Failed to load source file\n");
@@ -56,7 +56,7 @@ static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
return -1;
}
unsigned char* bytecode = Toy_compileString(source, &fileSize);
const unsigned char* bytecode = Toy_compileString(source, &fileSize);
free((void*)source);
if (!bytecode) {
@@ -102,70 +102,19 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
Toy_freeLiteral(drivePathLiteralIdn);
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
Toy_Literal filePathLiteral = Toy_getDrivePathLiteral(interpreter, &drivePathLiteral);
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
int driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n");
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
//check for file extensions
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) {
interpreter->errorOutput("Bad binary file extension (expected .tb)\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
//check for break-out attempts
for (int i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
}
//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;
@@ -192,7 +141,8 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
TOY_FREE_ARRAY(char, filePath, realLength);
//free the drive path
Toy_freeLiteral(filePathLiteral);
return 1;
}
@@ -200,7 +150,7 @@ static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArr
static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
interpreter->errorOutput("Incorrect number of arguments to runScript\n");
return -1;
}
@@ -213,7 +163,7 @@ static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
interpreter->errorOutput("Unrecognized opaque literal in runScript\n");
return -1;
}
@@ -241,7 +191,7 @@ static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n");
interpreter->errorOutput("Incorrect number of arguments to getScriptVar\n");
return -1;
}
@@ -260,7 +210,7 @@ static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
interpreter->errorOutput("Unrecognized opaque literal in getScriptVar\n");
return -1;
}
@@ -292,7 +242,7 @@ static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count < 2) {
interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n");
interpreter->errorOutput("Incorrect number of arguments to callScriptFn\n");
return -1;
}
@@ -309,7 +259,7 @@ static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
Toy_LiteralArray rest;
Toy_initLiteralArray(&rest);
while (tmp.count) { //correct the order of the rest args
while (tmp.count > 0) { //correct the order of the rest args
Toy_Literal lit = Toy_popLiteralArray(&tmp);
Toy_pushLiteralArray(&rest, lit);
Toy_freeLiteral(lit);
@@ -317,7 +267,6 @@ static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
Toy_freeLiteralArray(&tmp);
//get the runner object
Toy_Literal varName = Toy_popLiteralArray(arguments);
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
@@ -333,7 +282,7 @@ static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
interpreter->errorOutput("Unrecognized opaque literal in callScriptFn\n");
return -1;
}
@@ -389,7 +338,7 @@ static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* ar
static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _resetScript\n");
interpreter->errorOutput("Incorrect number of arguments to resetScript\n");
return -1;
}
@@ -402,7 +351,7 @@ static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arg
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
interpreter->errorOutput("Unrecognized opaque literal in resetScript\n");
return -1;
}
@@ -425,7 +374,7 @@ static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arg
static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _freeScript\n");
interpreter->errorOutput("Incorrect number of arguments to freeScript\n");
return -1;
}
@@ -438,7 +387,7 @@ static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _freeScript\n");
interpreter->errorOutput("Unrecognized opaque literal in freeScript\n");
return -1;
}
@@ -459,7 +408,7 @@ static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* argu
static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
interpreter->errorOutput("Incorrect number of arguments to checkScriptDirty\n");
return -1;
}
@@ -472,7 +421,7 @@ static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
interpreter->errorOutput("Unrecognized opaque literal in checkScriptDirty\n");
return -1;
}
@@ -492,7 +441,7 @@ static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray
//call the hook
typedef struct Natives {
char* name;
const char* name;
Toy_NativeFn fn;
} Natives;
@@ -501,19 +450,19 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
Natives natives[] = {
{"loadScript", nativeLoadScript},
{"loadScriptBytecode", nativeLoadScriptBytecode},
{"_runScript", nativeRunScript},
{"_getScriptVar", nativeGetScriptVar},
{"_callScriptFn", nativeCallScriptFn},
{"_resetScript", nativeResetScript},
{"_freeScript", nativeFreeScript},
{"_checkScriptDirty", nativeCheckScriptDirty},
{"runScript", nativeRunScript},
{"getScriptVar", nativeGetScriptVar},
{"callScriptFn", nativeCallScriptFn},
{"resetScript", nativeResetScript},
{"freeScript", nativeFreeScript},
{"checkScriptDirty", nativeCheckScriptDirty},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
@@ -560,87 +509,3 @@ int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
return 0;
}
//file system API
static Toy_LiteralDictionary Toy_driveDictionary;
void Toy_initDriveDictionary() {
Toy_initLiteralDictionary(&Toy_driveDictionary);
}
void Toy_freeDriveDictionary() {
Toy_freeLiteralDictionary(&Toy_driveDictionary);
}
Toy_LiteralDictionary* Toy_getDriveDictionary() {
return &Toy_driveDictionary;
}
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
int driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
//check for break-out attempts
for (int i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return result;
}
+1 -9
View File
@@ -2,14 +2,6 @@
#include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
//file system API - these need to be set by the host
void Toy_initDriveDictionary();
void Toy_freeDriveDictionary();
Toy_LiteralDictionary* Toy_getDriveDictionary();
#define TOY_OPAQUE_TAG_RUNNER 100
//file system API - for use with other libs
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+2132 -3
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -3,4 +3,3 @@
#include "toy_interpreter.h"
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
-411
View File
@@ -1,411 +0,0 @@
#include "lib_timer.h"
#include "toy_memory.h"
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
//normallize
if (x->tv_usec > 999999) {
x->tv_sec += x->tv_usec / 1000000;
x->tv_usec %= 1000000;
}
if (y->tv_usec > 999999) {
y->tv_sec += y->tv_usec / 1000000;
y->tv_usec %= 1000000;
}
//calc
result->tv_sec = x->tv_sec - y->tv_sec;
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
if (result->tv_sec != 0) { //only works far from 0
result->tv_usec += 1000000;
result->tv_sec--; // borrow
}
}
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
}
//god damn it
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
struct timeval* d = TOY_ALLOCATE(struct timeval, 1);
//I gave up, copied from SO
timeval_subtract(d, rhs, lhs);
return d;
}
//callbacks
static int nativeStartTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
gettimeofday(timeinfo, NULL);
//wrap in an opaque literal for Toy
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
Toy_freeLiteral(timeLiteral);
return 1;
}
static int nativeStopTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval timerStop;
gettimeofday(&timerStop, NULL);
//unwrap the opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timerStart = TOY_AS_OPAQUE(timeLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(timerStart, &timerStop);
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(diffLiteral);
return 1;
}
static int nativeCreateTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
return -1;
}
//get the args
Toy_Literal microsecondLiteral = Toy_popLiteralArray(arguments);
Toy_Literal secondLiteral = Toy_popLiteralArray(arguments);
Toy_Literal secondLiteralIdn = secondLiteral;
if (TOY_IS_IDENTIFIER(secondLiteral) && Toy_parseIdentifierToValue(interpreter, &secondLiteral)) {
Toy_freeLiteral(secondLiteralIdn);
}
Toy_Literal microsecondLiteralIdn = microsecondLiteral;
if (TOY_IS_IDENTIFIER(microsecondLiteral) && Toy_parseIdentifierToValue(interpreter, &microsecondLiteral)) {
Toy_freeLiteral(microsecondLiteralIdn);
}
if (!TOY_IS_INTEGER(secondLiteral) || !TOY_IS_INTEGER(microsecondLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return -1;
}
if (TOY_AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || TOY_AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (TOY_AS_INTEGER(secondLiteral) != 0 && TOY_AS_INTEGER(microsecondLiteral) < 0) ) {
interpreter->errorOutput("Microseconds out of range in createTimer\n");
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return -1;
}
//get the timeinfo from toy
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
timeinfo->tv_sec = TOY_AS_INTEGER(secondLiteral);
timeinfo->tv_usec = TOY_AS_INTEGER(microsecondLiteral);
//wrap in an opaque literal for Toy
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return 1;
}
static int nativeGetTimerSeconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
return -1;
}
//unwrap the opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the result literal
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_sec);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(result);
return 1;
}
static int nativeGetTimerMicroseconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
return -1;
}
//unwrap the opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the result literal
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_usec);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(result);
return 1;
}
static int nativeCompareTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
return -1;
}
//unwrap the opaque literals
Toy_Literal rhsLiteral = Toy_popLiteralArray(arguments);
Toy_Literal lhsLiteral = Toy_popLiteralArray(arguments);
Toy_Literal lhsLiteralIdn = lhsLiteral;
if (TOY_IS_IDENTIFIER(lhsLiteral) && Toy_parseIdentifierToValue(interpreter, &lhsLiteral)) {
Toy_freeLiteral(lhsLiteralIdn);
}
Toy_Literal rhsLiteralIdn = rhsLiteral;
if (TOY_IS_IDENTIFIER(rhsLiteral) && Toy_parseIdentifierToValue(interpreter, &rhsLiteral)) {
Toy_freeLiteral(rhsLiteralIdn);
}
if (!TOY_IS_OPAQUE(lhsLiteral) || !TOY_IS_OPAQUE(rhsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
Toy_freeLiteral(lhsLiteral);
Toy_freeLiteral(rhsLiteral);
return -1;
}
struct timeval* lhsTimer = TOY_AS_OPAQUE(lhsLiteral);
struct timeval* rhsTimer = TOY_AS_OPAQUE(rhsLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(lhsTimer, rhsTimer);
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
Toy_freeLiteral(lhsLiteral);
Toy_freeLiteral(rhsLiteral);
Toy_freeLiteral(diffLiteral);
return 1;
}
static int nativeTimerToString(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
return -1;
}
//unwrap in an opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the string literal
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
char buffer[128];
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
}
else { //normal case
char buffer[128];
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
}
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeDestroyTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
return -1;
}
//unwrap in an opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
TOY_FREE(struct timeval, timer);
Toy_freeLiteral(timeLiteral);
return 0;
}
//call the hook
typedef struct Natives {
char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"startTimer", nativeStartTimer},
{"_stopTimer", nativeStopTimer},
{"createTimer", nativeCreateTimer},
{"_getTimerSeconds", nativeGetTimerSeconds},
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
{"_compareTimer", nativeCompareTimer},
{"_timerToString", nativeTimerToString},
{"_destroyTimer", nativeDestroyTimer},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
@@ -1,23 +1,23 @@
#include "lib_about.h"
#include "lib_toy_version_info.h"
#include "toy_memory.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//the about keys
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 about identifiers
//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 about values
//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);
@@ -27,7 +27,7 @@ int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
//store as an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
if (Toy_isDeclaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
@@ -83,11 +83,11 @@ int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lite
//store globally
else {
//make sure the names aren't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
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);
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookToyVersionInfo(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+1 -1
View File
@@ -2,7 +2,7 @@ CC=gcc
IDIR+=. ../source
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=-ltoy
LIBS+=-ltoy -lm
ODIR = obj
SRC = $(wildcard *.c)
+92 -39
View File
@@ -1,45 +1,47 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_compound.h"
#include "drive_system.h"
#include "lib_toy_version_info.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "lib_fileio.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 "toy.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void repl() {
#define INPUT_BUFFER_SIZE 2048
void repl(const char* initialInput) {
//repl does it's own thing for now
bool error = false;
const int size = 2048;
char input[size];
memset(input, 0, size);
char input[INPUT_BUFFER_SIZE];
memset(input, 0, INPUT_BUFFER_SIZE);
Toy_Interpreter interpreter; //persist the interpreter for the scopes
Toy_initInterpreter(&interpreter);
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
Toy_injectNativeHook(&interpreter, "fileio", Toy_hookFileIO);
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
for(;;) {
printf("> ");
//handle EOF for exits
if (!fgets(input, size, stdin)) {
break;
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)
@@ -52,7 +54,8 @@ void repl() {
Toy_Parser parser;
Toy_Compiler compiler;
Toy_initLexer(&lexer, input);
Toy_initLexer(&lexer, initialInput ? initialInput : input);
Toy_private_setComments(&lexer, initialInput != NULL); //BUGFIX: disable comments here
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
@@ -76,7 +79,7 @@ void repl() {
if (!error) {
//get the bytecode dump
int size = 0;
size_t size = 0;
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
//run the bytecode
@@ -87,6 +90,15 @@ void repl() {
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
error = false;
if (initialInput) {
free((void*)initialInput);
initialInput = NULL;
if (interpreter.panic) {
break;
}
}
}
Toy_freeInterpreter(&interpreter);
@@ -96,16 +108,9 @@ void repl() {
int main(int argc, const char* argv[]) {
Toy_initCommandLine(argc, argv);
//lib setup (hacky - only really for this program)
Toy_initDriveDictionary();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//setup the drive system (for filesystem access)
Toy_initDriveSystem();
Toy_setDrivePath("scripts", "scripts");
//command line specific actions
if (Toy_commandLine.error) {
@@ -130,10 +135,18 @@ int main(int argc, const char* argv[]) {
//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_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
@@ -143,19 +156,32 @@ int main(int argc, const char* argv[]) {
Toy_runSource(Toy_commandLine.source);
//lib cleanup
Toy_freeDriveDictionary();
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;
char* source = Toy_readFile(Toy_commandLine.compilefile, &size);
const char* source = (const char*)Toy_readFile(Toy_commandLine.compilefile, &size);
if (!source) {
return 1;
}
unsigned char* tb = Toy_compileString(source, &size);
const unsigned char* tb = Toy_compileString(source, &size);
if (!tb) {
return 1;
}
@@ -165,18 +191,45 @@ int main(int argc, const char* argv[]) {
//run binary
if (Toy_commandLine.binaryfile) {
Toy_runBinaryFile(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_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
repl();
const char* initialSource = NULL;
if (Toy_commandLine.initialfile) {
//only works on toy files
const char* s = strrchr(Toy_commandLine.initialfile, '.');
if (!s || strcmp(s, ".toy")) {
fprintf(stderr, TOY_CC_ERROR "Bad file extension passed to %s (expected '.toy', found '%s')" TOY_CC_RESET, argv[0], s);
return -1;
}
size_t size;
initialSource = (const char*)Toy_readFile(Toy_commandLine.initialfile, &size);
}
repl(initialSource);
//lib cleanup
Toy_freeDriveDictionary();
Toy_freeDriveSystem();
return 0;
}
+82 -24
View File
@@ -1,9 +1,10 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_compound.h"
#include "lib_toy_version_info.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "lib_random.h"
#include "lib_runner.h"
#include "lib_fileio.h"
#include "lib_math.h"
#include "toy_console_colors.h"
@@ -16,7 +17,7 @@
#include <stdlib.h>
//IO functions
char* Toy_readFile(char* path, size_t* fileSize) {
const unsigned char* Toy_readFile(const char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
if (file == NULL) {
@@ -28,14 +29,14 @@ char* Toy_readFile(char* path, size_t* fileSize) {
*fileSize = ftell(file);
rewind(file);
char* buffer = (char*)malloc(*fileSize + 1);
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(char), *fileSize, file);
size_t bytesRead = fread(buffer, sizeof(unsigned char), *fileSize, file);
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
@@ -49,7 +50,7 @@ char* Toy_readFile(char* path, size_t* fileSize) {
return buffer;
}
int Toy_writeFile(char* path, unsigned char* bytes, size_t size) {
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
FILE* file = fopen(path, "wb");
if (file == NULL) {
@@ -57,7 +58,7 @@ int Toy_writeFile(char* path, unsigned char* bytes, size_t size) {
return -1;
}
int written = fwrite(bytes, size, 1, file);
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);
@@ -70,7 +71,7 @@ int Toy_writeFile(char* path, unsigned char* bytes, size_t size) {
}
//repl functions
unsigned char* Toy_compileString(char* source, size_t* size) {
const unsigned char* Toy_compileString(const char* source, size_t* size) {
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
@@ -79,10 +80,10 @@ unsigned char* Toy_compileString(char* source, size_t* size) {
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//run the parser until the end of the source
//step 1 - run the parser until the end of the source
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and leave
//on error, pack up and leave
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
@@ -95,8 +96,8 @@ unsigned char* Toy_compileString(char* source, size_t* size) {
node = Toy_scanParser(&parser);
}
//get the bytecode dump
unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
//step 2 - get the bytecode dump
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
//cleanup
Toy_freeCompiler(&compiler);
@@ -107,24 +108,25 @@ unsigned char* Toy_compileString(char* source, size_t* size) {
return tb;
}
void Toy_runBinary(unsigned char* tb, size_t size) {
void Toy_runBinary(const unsigned char* tb, size_t size) {
Toy_Interpreter interpreter;
Toy_initInterpreter(&interpreter);
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
Toy_injectNativeHook(&interpreter, "toy_version_info", Toy_hookToyVersionInfo);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
Toy_injectNativeHook(&interpreter, "random", Toy_hookRandom);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
Toy_injectNativeHook(&interpreter, "fileio", Toy_hookFileIO);
Toy_injectNativeHook(&interpreter, "math", Toy_hookMath);
Toy_runInterpreter(&interpreter, tb, size);
Toy_runInterpreter(&interpreter, tb, (int)size);
Toy_freeInterpreter(&interpreter);
}
void Toy_runBinaryFile(char* fname) {
void Toy_runBinaryFile(const char* fname) {
size_t size = 0; //not used
unsigned char* tb = (unsigned char*)Toy_readFile(fname, &size);
const unsigned char* tb = Toy_readFile(fname, &size);
if (!tb) {
return;
}
@@ -132,9 +134,9 @@ void Toy_runBinaryFile(char* fname) {
//interpreter takes ownership of the binary data
}
void Toy_runSource(char* source) {
void Toy_runSource(const char* source) {
size_t size = 0;
unsigned char* tb = Toy_compileString(source, &size);
const unsigned char* tb = Toy_compileString(source, &size);
if (!tb) {
return;
}
@@ -142,12 +144,68 @@ void Toy_runSource(char* source) {
Toy_runBinary(tb, size);
}
void Toy_runSourceFile(char* fname) {
void Toy_runSourceFile(const char* fname) {
size_t size = 0; //not used
char* source = Toy_readFile(fname, &size);
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);
}
+77 -7
View File
@@ -1,14 +1,84 @@
#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"
char* Toy_readFile(char* path, size_t* fileSize);
int Toy_writeFile(char* path, unsigned char* bytes, size_t size);
/*!
## Defined Functions
!*/
unsigned char* Toy_compileString(char* source, size_t* size);
/*!
### const char* Toy_readFile(const char* path, size_t* fileSize)
void Toy_runBinary(unsigned char* tb, size_t size);
void Toy_runBinaryFile(char* fname);
void Toy_runSource(char* source);
void Toy_runSourceFile(char* fname);
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);
-125
View File
@@ -1,125 +0,0 @@
import node;
//constants
var SPEED: int const = 10;
//variables
var parent: opaque = null;
var posX: int = 50;
var posY: int = 50;
var WIDTH: int const = 100;
var HEIGHT: int const = 100;
var xspeed: int = 0;
var yspeed: int = 0;
//accessors - variables are private, functions are public
fn getX(node: opaque) {
return posX;
}
fn getY(node: opaque) {
return posY;
}
//lifecycle functions
fn onInit(node: opaque) {
print "render.toy:onInit() called";
node.loadTexture("sprites:/character.png");
parent = node.getNodeParent();
}
fn onStep(node: opaque) {
posX += xspeed;
posY += yspeed;
}
fn onFree(node: opaque) {
print "render.toy:onFree() called";
node.freeTexture();
}
fn onDraw(node: opaque) {
// print "render.toy:onDraw() called";
var px = parent.callNode("getX");
var py = parent.callNode("getY");
if (px == null) {
px = 0;
}
if (py == null) {
py = 0;
}
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
}
//event functions
fn onKeyDown(node: opaque, event: string) {
if (event == "character_up") {
yspeed -= SPEED;
return;
}
if (event == "character_down") {
yspeed += SPEED;
return;
}
if (event == "character_left") {
xspeed -= SPEED;
return;
}
if (event == "character_right") {
xspeed += SPEED;
return;
}
}
fn onKeyUp(node: opaque, event: string) {
if (event == "character_up" && yspeed < 0) {
yspeed = 0;
return;
}
if (event == "character_down" && yspeed > 0) {
yspeed = 0;
return;
}
if (event == "character_left" && xspeed < 0) {
xspeed = 0;
return;
}
if (event == "character_right" && xspeed > 0) {
xspeed = 0;
return;
}
}
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")";
}
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")";
//jump to pos
posX = x - WIDTH / 2;
posY = y - HEIGHT / 2;
}
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")";
}
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")";
}
-89
View File
@@ -1,89 +0,0 @@
//single line comment
/*
multi line comment
*/
//test primitive literals
print "hello world";
print null;
print true;
print false;
print 42;
print 3.14;
print -69;
print -4.20;
print 2 + (3 * 3);
//test operators (integers)
print 1 + 1;
print 1 - 1;
print 2 * 2;
print 1 / 2;
print 4 % 2;
//test operators (floats)
print 1.0 + 1.0;
print 1.0 - 1.0;
print 2.0 * 2.0;
print 1.0 / 2.0;
//test scopes
{
print "This statement is within a scope.";
{
print "This is a deeper scope.";
}
}
print "Back to the outer scope.";
//test scope will delegate to higher scope
var a = 1;
{
a = 2;
print a;
}
print a;
//test scope will shadow higher scope on redefine
var b: int = 3;
{
var b = 4;
print b;
}
print b;
//test compounds, repeatedly
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
//test empties
print [];
print [:];
//test nested compounds
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
//var declarations
var x = 31;
var y : int = 42;
var arr : [int] = [1, 2, 3, 42];
var dict : [string:int] = ["hello": 1, "world":2];
//printing expressions
print x;
print x + y;
print arr;
print dict;
//test asserts at the end of the file
assert x, "This won't be seen";
assert true, "This won't be seen";
assert false, "This is a failed assert, and will end execution";
print "This will not be printed because of the above assert";
+2 -1
View File
@@ -1,9 +1,10 @@
//WARNING: please think twice before using this in a test
fn fib(n : int) {
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
for (var i = 0; i < 20; i++) {
for (var i = 0; i <= 35; i++) {
var res = fib(i);
print string i + ": " + string res;
}
+90
View File
@@ -0,0 +1,90 @@
/*
How to run this program:
toyrepl -n -t scripts/level.toy
How to move around:
move(up);
move(down);
move(left);
move(right);
*/
//constants
var WIDTH: int const = 12;
var HEIGHT: int const = 12;
//WIDTH * HEIGHT in size
var tiles: [[int]] const = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] //BUG: map is twisted along this diagonal
];
var tileset: [int: string] const = [
0: " ",
1: "X "
];
//variables
var posX: int = 4;
var posY: int = 4;
//functions
fn draw() {
for (var j: int = 0; j < HEIGHT; j++) {
for (var i: int = 0; i < WIDTH; i++) {
//draw the player pos
if (i == posX && j == posY) {
print "O ";
continue;
}
print tileset[ tiles[i][j] ];
}
print "\n";
}
print "\n";
}
fn moveRelative(xrel: int, yrel: int) {
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1 || (xrel != 0 && yrel != 0)) {
print "too fast!\n";
return;
}
if (tiles[posX + xrel][posY + yrel] > 0) {
print "Can't move that way\n";
return;
}
posX += xrel;
posY += yrel;
draw();
}
//wrap for easy use
var up: [int] const = [0, -1];
var down: [int] const = [0, 1];
var left: [int] const = [-1, 0];
var right: [int] const = [1, 0];
fn move(dir: [int] const) {
return moveRelative(dir[0], dir[1]);
}
//initial display
move([0, 0]);
+36
View File
@@ -0,0 +1,36 @@
/*
Since this is a pseudo-random generator, and there's no internal state to the algorithm other
than the generator opaque, there needs to be a "call counter" (current depth) to shuffle the
initial seeds, otherwise generators created from other generators will resemble their parents,
but one call greater.
*/
import standard;
import random;
var DEPTH: int const = 20;
var levels = [];
//generate the level seeds
var generator: opaque = createRandomGenerator(clock().hash());
for (var i: int = 0; i < DEPTH; i++) {
levels.push(generator.generateRandomNumber());
}
generator.freeRandomGenerator();
//generate "levels" of a roguelike
for (var i = 0; i < DEPTH; i++) {
var rng: opaque = createRandomGenerator(levels[i] + i);
print "---";
print levels[i];
print rng.generateRandomNumber();
print rng.generateRandomNumber();
print rng.generateRandomNumber();
rng.freeRandomGenerator();
}
+1 -1
View File
@@ -1,5 +1,5 @@
//number of iterations
var SIZE: int const = 260;
var SIZE: int const = 100;
//lookup table
var lookup = [
+61
View File
@@ -0,0 +1,61 @@
import math;
import fileio;
import standard;
fn circle(radius: int, centerX: int, centerY: int) {
for (var y: int = 0; y <= 2 * radius; y++) {
for (var x: int = 0; x <= 2 * radius; x++) {
var distance: int = round(sqrt((x - radius) * (x - radius) + (y - radius) * (y - radius)));
if (distance <= radius) {
output.write("*");
} else {
output.write(" ");
}
}
output.write("\n");
}
}
fn triangle(height: int) {
for (var i: int = 1; i <= height; i++) {
for (var j: int = 1; j <= height - i; j++) {
output.write(" ");
}
for (var j: int = 1; j <= 2 * i - 1; j++) {
output.write("*");
}
output.write("\n");
}
}
fn square(size: int) {
for (var i: int = 0; i < size; ++i) {
for (var j: int = 0; j < size; ++j) {
output.write("* ");
}
output.write("\n");
}
}
while (true) {
output.write("Enter:\n0\tfor circle\n1\tfor triangle\n2\tfor square\n> ");
var result: any = input.read(int);
if (result == 0) {
circle(2, 5, 5);
}
else if (result == 1) {
triangle(5);
}
else if (result == 2) {
square(5);
}
else {
output.write("invalid input :(\n");
}
}
-6
View File
@@ -1,6 +0,0 @@
import compound;
var arr: [int] = [1, 2, 3];
fn f(_, v: int): int { return v + 1; }
print arr.map(f);
+13
View File
@@ -0,0 +1,13 @@
fn f() {
//
}
fn g() {
fn i() {
//
}
}
fn h() {
//
}
+3 -3
View File
@@ -11,14 +11,14 @@ OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUTNAME=toy
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Linux)
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
CFLAGS += -fPIC
else ifeq ($(OS),Windows_NT)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
LIBLINE=-Wl,-rpath,. -Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Darwin)
LIBLINE = $(OBJ)
+87
View File
@@ -0,0 +1,87 @@
#pragma once
/*!
# toy.h - A Toy Programming Language
If you're looking how to use Toy directly, try https://toylang.com/
Otherwise, this header may help learn how Toy works internally.
!*/
/*!
## Utilities
These headers define a bunch of useful macros, based on what platform you build for.
The most important macro is `TOY_API`, which specifies functions intended for the end user.
* [toy_common.h](toy_common_h.md)
* [toy_console_colors.h](toy_console_colors_h.md)
* [toy_memory.h](toy_memory_h.md)
!*/
#include "toy_common.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
/*!
## Core Pipeline
From source to execution, each step is as follows:
```
source -> lexer -> token
token -> parser -> AST
AST -> compiler -> bytecode
bytecode -> interpreter -> result
```
I should note that the parser -> compiler phase is actually made up of two steps - the write step and the collate step. See `Toy_compileString()` in `repl/repl_tools.c` for an example of how to compile properly.
* [toy_lexer.h](toy_lexer_h.md)
* [toy_parser.h](toy_parser_h.md)
* [toy_compiler.h](toy_compiler_h.md)
* [toy_interpreter.h](toy_interpreter_h.md)
!*/
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
/*!
## Building Block Structures
Literals represent any value within the language, including some internal ones that you never see.
Literal arrays are contiguous arrays within memory, and are the most heavily used structure in Toy.
Literal dictionaries are unordered key-value hashmaps, that use a running strategy for collisions.
* [toy_literal.h](toy_literal_h.md)
* [toy_literal_array.h](toy_literal_array_h.md)
* [toy_literal_dictionary.h](toy_literal_dictionary_h.md)
!*/
#include "toy_literal.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
/*!
## Other Components
You probably won't use these directly, but they're a good learning opportunity.
`Toy_Scope` holds the variables of a specific scope within Toy - be it a script, a function, a block, etc. Scopes are also where the type system lives at runtime. They use identifier literals as keys, exclusively.
`Toy_RefString` is a utility class that wraps traditional C strings, making them less memory intensive and faster to copy and move. In reality, since strings are considered immutable, multiple variables can point to the same string to save memory, and you can just create a new one of these vars pointing to the original rather than copying entirely for a speed boost. This module has it's own memory allocator system that is plugged into the main memory allocator.
`Toy_RefFunction` acts similarly to `Toy_RefString`, but instead operates on function bytecode.
* [toy_scope.h](toy_scope_h.md)
* [toy_refstring.h](toy_refstring_h.md)
* [toy_reffunction.h](toy_reffunction_h.md)
!*/
#include "toy_scope.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
+57 -9
View File
@@ -40,17 +40,21 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
break;
case TOY_AST_NODE_BLOCK:
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
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);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
break;
case TOY_AST_NODE_COMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
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);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
break;
case TOY_AST_NODE_PAIR:
@@ -71,10 +75,12 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
break;
case TOY_AST_NODE_FN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
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);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case TOY_AST_NODE_FN_DECL:
@@ -118,6 +124,16 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
//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;
@@ -135,6 +151,10 @@ static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
Toy_freeLiteral(node->import.identifier);
Toy_freeLiteral(node->import.alias);
break;
case TOY_AST_NODE_PASS:
//EMPTY
break;
}
if (freeSelf) {
@@ -338,6 +358,26 @@ void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
*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->binary.left = *nodeHandle;
tmp->binary.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->binary.left = *nodeHandle;
tmp->binary.right = rhs;
*nodeHandle = tmp;
}
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
@@ -383,3 +423,11 @@ void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy
*nodeHandle = tmp;
}
void Toy_emitASTNodePass(Toy_ASTNode** nodeHandle) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_PASS;
*nodeHandle = tmp;
}
+27
View File
@@ -29,11 +29,14 @@ typedef enum Toy_ASTNodeType {
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
@@ -203,6 +206,24 @@ 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);
@@ -238,6 +259,9 @@ typedef struct Toy_NodeImport {
Toy_Literal alias;
} Toy_NodeImport;
//for doing nothing
void Toy_emitASTNodePass(Toy_ASTNode** nodeHandle);
union Toy_private_node {
Toy_ASTNodeType type;
Toy_NodeLiteral atomic;
@@ -259,6 +283,8 @@ union Toy_private_node {
Toy_NodeFor pathFor;
Toy_NodeBreak pathBreak;
Toy_NodeContinue pathContinue;
Toy_NodeAnd pathAnd;
Toy_NodeOr pathOr;
Toy_NodePrefixIncrement prefixIncrement;
Toy_NodePrefixDecrement prefixDecrement;
Toy_NodePostfixIncrement postfixIncrement;
@@ -266,4 +292,5 @@ union Toy_private_node {
Toy_NodeImport import;
};
//see toy_parser.h for more documentation on this function
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
+160 -40
View File
@@ -4,6 +4,7 @@
#include "toy_literal.h"
#include <stdio.h>
#include <string.h>
//static math utils, copied from the interpreter
static Toy_Literal addition(Toy_Interpreter* interpreter, Toy_Literal lhs, Toy_Literal rhs) {
@@ -278,6 +279,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//second and third are bad args to dictionaries
if (!TOY_IS_NULL(second) || !TOY_IS_NULL(third)) {
interpreter->errorOutput("Index slicing not allowed for dictionaries\n");
@@ -400,6 +412,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
interpreter->errorOutput("Bad first indexing\n");
@@ -542,6 +565,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_ARRAY(compound)->count) {
interpreter->errorOutput("Bad first indexing assignment\n");
@@ -587,7 +621,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
//simple indexing assignment if second is null
if (TOY_IS_NULL(second)) {
bool ret = -1;
int ret = -1;
if (!Toy_setLiteralArray(TOY_AS_ARRAY(compound), first, assign)) {
interpreter->errorOutput("Array index out of bounds in assignment");
@@ -595,6 +629,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
}
else {
Toy_pushLiteralArray(&interpreter->stack, compound); //leave the array on the stack
//...
ret = 1;
}
@@ -704,6 +739,17 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
Toy_Literal value = Toy_getLiteralArray(TOY_AS_ARRAY(compound), first);
if (TOY_IS_STRING(op) && Toy_equalsRefStringCString(TOY_AS_STRING(op), "+=")) {
@@ -792,8 +838,19 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_STRING(compound)->length) {
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
interpreter->errorOutput("Bad first indexing in string\n");
//something is weird - skip out
@@ -807,7 +864,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
return -1;
}
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= TOY_AS_STRING(compound)->length) {
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
interpreter->errorOutput("Bad second indexing in string\n");
//something is weird - skip out
@@ -838,7 +895,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
//simple indexing if second is null
if (TOY_IS_NULL(second)) {
char* cstr = Toy_toCString(TOY_AS_STRING(compound));
const char* cstr = Toy_toCString(TOY_AS_STRING(compound));
char buf[16];
snprintf(buf, 16, "%s", &(cstr[ TOY_AS_INTEGER(first) ]) );
@@ -936,8 +993,19 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_freeLiteral(idn);
}
if (TOY_IS_IDENTIFIER(first) || TOY_IS_IDENTIFIER(second) || TOY_IS_IDENTIFIER(third)) {
Toy_freeLiteral(op);
Toy_freeLiteral(assign);
Toy_freeLiteral(third);
Toy_freeLiteral(second);
Toy_freeLiteral(first);
Toy_freeLiteral(compound);
return -1;
}
//handle each error case
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= TOY_AS_STRING(compound)->length) {
if (!TOY_IS_INTEGER(first) || TOY_AS_INTEGER(first) < 0 || TOY_AS_INTEGER(first) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
interpreter->errorOutput("Bad first indexing in string assignment\n");
//something is weird - skip out
@@ -951,7 +1019,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
return -1;
}
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= TOY_AS_STRING(compound)->length) {
if ((!TOY_IS_NULL(second) && !TOY_IS_INTEGER(second)) || TOY_AS_INTEGER(second) < 0 || TOY_AS_INTEGER(second) >= (int)Toy_lengthRefString(TOY_AS_STRING(compound))) {
interpreter->errorOutput("Bad second indexing in string assignment\n");
//something is weird - skip out
@@ -1063,7 +1131,7 @@ int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 3) {
interpreter->errorOutput("Incorrect number of arguments to _set\n");
interpreter->errorOutput("Incorrect number of arguments to set\n");
return -1;
}
@@ -1073,12 +1141,16 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_Literal val = arguments->literals[2];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("Expected identifier in _set\n");
interpreter->errorOutput("Expected identifier in set\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
bool freeKey = false;
if (TOY_IS_IDENTIFIER(key)) {
Toy_parseIdentifierToValue(interpreter, &key);
@@ -1091,26 +1163,40 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
freeVal = true;
}
if (TOY_IS_IDENTIFIER(key) || TOY_IS_IDENTIFIER(val)) {
if (freeKey) {
Toy_freeLiteral(key);
}
if (freeVal) {
Toy_freeLiteral(val);
}
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, key);
//check the subtype of the array, if there is one, against the given argument
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, idn);
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
interpreter->errorOutput("Bad argument type in _set\n");
interpreter->errorOutput("Bad argument type in set\n");
Toy_freeLiteral(typeLiteral);
return -1;
}
}
Toy_freeLiteral(typeLiteral);
if (!TOY_IS_INTEGER(key)) {
interpreter->errorOutput("Expected integer index in _set\n");
interpreter->errorOutput("Expected integer index in set\n");
return -1;
}
if (TOY_AS_ARRAY(obj)->count <= TOY_AS_INTEGER(key) || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in _set\n");
if (TOY_AS_INTEGER(key) >= TOY_AS_ARRAY(obj)->count || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in set\n");
return -1;
}
@@ -1119,7 +1205,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
TOY_AS_ARRAY(obj)->literals[TOY_AS_INTEGER(key)] = Toy_copyLiteral(val);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to array in _set: \"");
interpreter->errorOutput("Incorrect type assigned to array in set: \"");
Toy_printLiteralCustom(val, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1136,12 +1222,12 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_Literal valSubtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1];
if (TOY_AS_TYPE(keySubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(keySubtypeLiteral).typeOf != key.type) {
interpreter->printOutput("bad argument type in _set\n");
interpreter->printOutput("bad argument type in set\n");
return -1;
}
if (TOY_AS_TYPE(valSubtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(valSubtypeLiteral).typeOf != val.type) {
interpreter->printOutput("bad argument type in _set\n");
interpreter->printOutput("bad argument type in set\n");
return -1;
}
}
@@ -1149,7 +1235,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_setLiteralDictionary(TOY_AS_DICTIONARY(obj), key, val);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to dictionary in _set: \"");
interpreter->errorOutput("Incorrect type assigned to dictionary in set: \"");
Toy_printLiteralCustom(val, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1159,7 +1245,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
}
default:
interpreter->errorOutput("Incorrect compound type in _set: ");
interpreter->errorOutput("Incorrect compound type in set: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1181,7 +1267,7 @@ int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _get");
interpreter->errorOutput("Incorrect number of arguments to get");
return -1;
}
@@ -1200,15 +1286,25 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
freeKey = true;
}
if (TOY_IS_IDENTIFIER(obj) || TOY_IS_IDENTIFIER(key)) {
if (freeObj) {
Toy_freeLiteral(obj);
}
if (freeKey) {
Toy_freeLiteral(key);
}
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
if (!TOY_IS_INTEGER(key)) {
interpreter->errorOutput("Expected integer index in _get\n");
interpreter->errorOutput("Expected integer index in get\n");
return -1;
}
if (TOY_AS_ARRAY(obj)->count <= TOY_AS_INTEGER(key) || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in _get\n");
if (TOY_AS_INTEGER(key) >= TOY_AS_ARRAY(obj)->count || TOY_AS_INTEGER(key) < 0) {
interpreter->errorOutput("Index out of bounds in get\n");
return -1;
}
@@ -1242,7 +1338,7 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
}
default:
interpreter->errorOutput("Incorrect compound type in _get \"");
interpreter->errorOutput("Incorrect compound type in get \"");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1252,7 +1348,7 @@ int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _push\n");
interpreter->errorOutput("Incorrect number of arguments to push\n");
return -1;
}
@@ -1261,35 +1357,47 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal val = arguments->literals[1];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("Expected identifier in _push\n");
interpreter->errorOutput("Expected identifier in push\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
bool freeVal = false;
if (TOY_IS_IDENTIFIER(val)) {
Toy_parseIdentifierToValue(interpreter, &val);
freeVal = true;
}
if (TOY_IS_IDENTIFIER(val)) {
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, val);
//check the subtype of the array, if there is one, against the given argument
Toy_Literal typeLiteral = Toy_getScopeType(interpreter->scope, idn);
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY) {
Toy_Literal subtypeLiteral = ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0];
if (TOY_AS_TYPE(subtypeLiteral).typeOf != TOY_LITERAL_ANY && TOY_AS_TYPE(subtypeLiteral).typeOf != val.type) {
interpreter->errorOutput("Bad argument type in _push");
interpreter->errorOutput("Bad argument type in push\n");
Toy_freeLiteral(typeLiteral);
return -1;
}
}
Toy_freeLiteral(typeLiteral);
Toy_pushLiteralArray(TOY_AS_ARRAY(obj), val);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
interpreter->errorOutput("Incorrect type assigned to array in _push: \"");
interpreter->errorOutput("Incorrect type assigned to array in push: \"");
Toy_printLiteralCustom(val, interpreter->errorOutput);
interpreter->errorOutput("\"\n");
return -1;
@@ -1305,7 +1413,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
}
default:
interpreter->errorOutput("Incorrect compound type in _push: ");
interpreter->errorOutput("Incorrect compound type in push: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1315,7 +1423,7 @@ int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _pop\n");
interpreter->errorOutput("Incorrect number of arguments to pop\n");
return -1;
}
@@ -1323,12 +1431,16 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_Literal obj = arguments->literals[0];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("Expected identifier in _pop\n");
interpreter->errorOutput("Expected identifier in pop\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal lit = Toy_popLiteralArray(TOY_AS_ARRAY(obj));
@@ -1336,7 +1448,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
Toy_freeLiteral(lit);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) { //TODO: could definitely be more efficient than overwriting the whole original object
interpreter->errorOutput("Incorrect type assigned to array in _pop: ");
interpreter->errorOutput("Incorrect type assigned to array in pop: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1348,7 +1460,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
}
default:
interpreter->errorOutput("Incorrect compound type in _pop: ");
interpreter->errorOutput("Incorrect compound type in pop: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1358,7 +1470,7 @@ int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _length\n");
interpreter->errorOutput("Incorrect number of arguments to length\n");
return -1;
}
@@ -1370,6 +1482,10 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
freeObj = true;
}
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
switch(obj.type) {
case TOY_LITERAL_ARRAY: {
Toy_Literal lit = TOY_TO_INTEGER_LITERAL( TOY_AS_ARRAY(obj)->count );
@@ -1393,7 +1509,7 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
}
default:
interpreter->errorOutput("Incorrect compound type in _length: ");
interpreter->errorOutput("Incorrect compound type in length: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1409,7 +1525,7 @@ int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//if wrong number of arguments, fail
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _clear\n");
interpreter->errorOutput("Incorrect number of arguments to clear\n");
return -1;
}
@@ -1417,12 +1533,16 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal obj = arguments->literals[0];
if (!TOY_IS_IDENTIFIER(idn)) {
interpreter->errorOutput("expected identifier in _clear\n");
interpreter->errorOutput("expected identifier in clear\n");
return -1;
}
Toy_parseIdentifierToValue(interpreter, &obj);
if (TOY_IS_IDENTIFIER(obj)) {
return -1;
}
//NOTE: just pass in new compounds
switch(obj.type) {
@@ -1433,7 +1553,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal obj = TOY_TO_ARRAY_LITERAL(array);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to array in _clear: ");
interpreter->errorOutput("Incorrect type assigned to array in clear: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1451,7 +1571,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
Toy_Literal obj = TOY_TO_DICTIONARY_LITERAL(dictionary);
if (!Toy_setScopeVariable(interpreter->scope, idn, obj, true)) {
interpreter->errorOutput("Incorrect type assigned to dictionary in _clear: ");
interpreter->errorOutput("Incorrect type assigned to dictionary in clear: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
@@ -1463,7 +1583,7 @@ int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
}
default:
interpreter->errorOutput("Incorrect compound type in _clear: ");
interpreter->errorOutput("Incorrect compound type in clear: ");
Toy_printLiteralCustom(obj, interpreter->errorOutput);
interpreter->errorOutput("\n");
return -1;
+55 -26
View File
@@ -4,7 +4,7 @@
#include <string.h>
#include <assert.h>
//test variable sizes based on platform
//test variable sizes based on platform - see issue #35
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(char) == 1);
@@ -15,23 +15,29 @@ STATIC_ASSERT(sizeof(unsigned char) == 1);
STATIC_ASSERT(sizeof(unsigned short) == 2);
STATIC_ASSERT(sizeof(unsigned int) == 4);
#ifndef TOY_EXPORT
static const char* build = __DATE__ " " __TIME__;
//declare the singleton
Toy_CommandLine Toy_commandLine;
const char* Toy_private_version_build() {
return build;
}
//declare the singleton with default values
Toy_CommandLine Toy_commandLine = {
.error = false,
.help = false,
.version = false,
.binaryfile = NULL,
.sourcefile = NULL,
.compilefile = NULL,
.outfile = "out.tb",
.source = NULL,
.initialfile = NULL,
.enablePrintNewline = true,
.parseBytecodeHeader = false,
.verbose = false
};
void Toy_initCommandLine(int argc, const char* argv[]) {
//default values
Toy_commandLine.error = false;
Toy_commandLine.help = false;
Toy_commandLine.version = false;
Toy_commandLine.binaryfile = NULL;
Toy_commandLine.sourcefile = NULL;
Toy_commandLine.compilefile = NULL;
Toy_commandLine.outfile = "out.tb";
Toy_commandLine.source = NULL;
Toy_commandLine.verbose = false;
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
@@ -81,6 +87,29 @@ void Toy_initCommandLine(int argc, const char* argv[]) {
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) {
@@ -96,20 +125,22 @@ void Toy_initCommandLine(int argc, const char* argv[]) {
}
void Toy_usageCommandLine(int argc, const char* argv[]) {
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
printf("Usage: %s [ file.tb | -h | -v | -d | -f file.toy | -i source | -c file.toy -o out.tb | -t file.toy ]\n\n", argv[0]);
}
void Toy_helpCommandLine(int argc, const char* argv[]) {
Toy_usageCommandLine(argc, argv);
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
printf("-h\t| --help\t\tShow this help then exit.\n\n");
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
printf(" -h, --help\t\t\tShow this help then exit.\n");
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
printf(" -d, --debug\t\t\tBe verbose when operating.\n");
printf(" -f, --file filename\t\tParse, compile and execute the source file.\n");
printf(" -i, --input source\t\tParse, compile and execute this given string of source code.\n");
printf(" -c, --compile filename\tParse and compile the specified source file into an output file.\n");
printf(" -o, --output outfile\t\tName of the output file built with --compile (default: out.tb).\n");
printf(" -t, --initial filename\tStart the repl as normal, after first running the given file.\n");
printf(" -p\t\t\t\tParse the given bytecode's header, then exit (requires file.tb).\n");
printf(" -n\t\t\t\tDisable the newline character at the end of the print statement.\n");
}
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
@@ -121,5 +152,3 @@ void Toy_copyrightCommandLine(int argc, const char* argv[]) {
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
}
#endif
+81 -17
View File
@@ -1,28 +1,89 @@
#pragma once
/*!
# toy_common.h
This file is generally included in most header files within Toy, as it is where the TOY_API macro is defined. It also has some utilities intended for use only by the repl.
## Defined Macros
!*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 8
#define TOY_VERSION_PATCH 1
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
/*!
### TOY_API
This definition of this macro is platform-dependant, and used to enable cross-platform compilation of shared and static libraries.
!*/
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
//platform-specific specifications
#if defined(__linux__)
#define TOY_API extern
#elif defined(_WIN32) || defined(WIN32)
#define TOY_API
#elif defined(_MSC_VER)
#ifndef TOY_EXPORT
#define TOY_API __declspec(dllimport)
#else
#define TOY_API __declspec(dllexport)
#endif
#else
#define TOY_API
#define TOY_API extern
#endif
#ifndef TOY_EXPORT
//for processing the command line arguments
/*!
### TOY_VERSION_MAJOR
The current major version of Toy. This value is embedded into the bytecode, and the interpreter will refuse to run bytecode with a major version that does not match its own version.
This value MUST fit into an unsigned char.
!*/
#define TOY_VERSION_MAJOR 1
/*!
### TOY_VERSION_MINOR
The current minor version of Toy. This value is embedded into the bytecode, and the interpreter will refuse to run bytecode with a minor version that is greater than its own minor version.
This value MUST fit into an unsigned char.
!*/
#define TOY_VERSION_MINOR 3
/*!
### TOY_VERSION_PATCH
The current patch version of Toy. This value is embedded into the bytecode.
This value MUST fit into an unsigned char.
!*/
#define TOY_VERSION_PATCH 1
/*!
### TOY_VERSION_BUILD
The current build version of Toy. This value is embedded into the bytecode.
This evaluates to a c-string, which contains build information such as compilation date and time of the interpreter. When in verbose mode, the compiler will display a warning if the build version of the bytecode does not match the build version of the interpreter.
This macro may also be used to store additonal information about forks of the Toy codebase.
!*/
#define TOY_VERSION_BUILD Toy_private_version_build()
TOY_API const char* Toy_private_version_build();
/*
The following code is intended only for use within the repl.
*/
//for processing the command line arguments in the repl
typedef struct {
bool error;
bool help;
@@ -32,14 +93,17 @@ typedef struct {
char* compilefile;
char* outfile; //defaults to out.tb
char* source;
char* initialfile;
bool enablePrintNewline;
bool parseBytecodeHeader;
bool verbose;
} Toy_CommandLine;
extern Toy_CommandLine Toy_commandLine;
//these are intended for the repl only, despite using the api prefix
TOY_API Toy_CommandLine Toy_commandLine;
void Toy_initCommandLine(int argc, const char* argv[]);
TOY_API void Toy_initCommandLine(int argc, const char* argv[]);
void Toy_usageCommandLine(int argc, const char* argv[]);
void Toy_helpCommandLine(int argc, const char* argv[]);
void Toy_copyrightCommandLine(int argc, const char* argv[]);
#endif
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[]);
+215 -32
View File
@@ -9,6 +9,7 @@
#include "toy_console_colors.h"
#include <stdio.h>
#include <string.h>
void Toy_initCompiler(Toy_Compiler* compiler) {
Toy_initLiteralArray(&compiler->literalCache);
@@ -47,7 +48,7 @@ static int writeLiteralTypeToCache(Toy_LiteralArray* literalCache, Toy_Literal l
}
//optimisation: check if exactly this literal array exists
int index = Toy_findLiteralIndex(literalCache, literal);
int index = Toy_private_findLiteralIndex(literalCache, literal);
if (index < 0) {
index = Toy_pushLiteralArray(literalCache, literal);
}
@@ -73,7 +74,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
switch(node->compound.nodes[i].pair.left->type) {
case TOY_AST_NODE_LITERAL: {
//keys are literals
int key = Toy_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
int key = Toy_private_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
if (key < 0) {
key = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
}
@@ -102,7 +103,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
switch(node->compound.nodes[i].pair.right->type) {
case TOY_AST_NODE_LITERAL: {
//values are literals
int val = Toy_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
int val = Toy_private_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
if (val < 0) {
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
}
@@ -129,7 +130,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
}
//push the store to the cache, with instructions about how pack it
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL(store);
Toy_Literal literal = TOY_TO_DICTIONARY_LITERAL((Toy_LiteralDictionary*)store); //cast from array to dict, because it's intermediate
literal.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE; //god damn it - nested in a dictionary
index = Toy_pushLiteralArray(&compiler->literalCache, literal);
Toy_freeLiteral(literal);
@@ -141,7 +142,7 @@ static int writeNodeCompoundToCache(Toy_Compiler* compiler, Toy_ASTNode* node) {
switch(node->compound.nodes[i].type) {
case TOY_AST_NODE_LITERAL: {
//values
int val = Toy_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
int val = Toy_private_findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
if (val < 0) {
val = Toy_pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
}
@@ -229,7 +230,7 @@ static int writeNodeCollectionToCache(Toy_Compiler* compiler, Toy_ASTNode* node)
static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
//get the index
int index = Toy_findLiteralIndex(&compiler->literalCache, literal);
int index = Toy_private_findLiteralIndex(&compiler->literalCache, literal);
if (index < 0) {
if (TOY_IS_TYPE(literal)) {
@@ -258,6 +259,87 @@ static int writeLiteralToCompiler(Toy_Compiler* compiler, Toy_Literal literal) {
return index;
}
//BUGFIX: check to see if this node lies within this tree
bool checkNodeInTree(Toy_ASTNode* tree, Toy_ASTNode* node) {
if (tree == node) {
return true;
}
if (tree == NULL) {
return false;
}
switch(tree->type) {
case TOY_AST_NODE_UNARY:
return checkNodeInTree(tree->unary.child, node);
case TOY_AST_NODE_BINARY:
return checkNodeInTree(tree->binary.left, node) || checkNodeInTree(tree->binary.right, node);
case TOY_AST_NODE_TERNARY:
return checkNodeInTree(tree->ternary.condition, node) || checkNodeInTree(tree->ternary.thenPath, node) || checkNodeInTree(tree->ternary.elsePath, node);
case TOY_AST_NODE_GROUPING:
return checkNodeInTree(tree->grouping.child, node);
case TOY_AST_NODE_BLOCK:
return checkNodeInTree(tree->block.nodes, node);
case TOY_AST_NODE_COMPOUND:
return checkNodeInTree(tree->compound.nodes, node);
case TOY_AST_NODE_PAIR:
return checkNodeInTree(tree->pair.left, node) || checkNodeInTree(tree->pair.right, node);
case TOY_AST_NODE_INDEX:
return checkNodeInTree(tree->index.first, node) || checkNodeInTree(tree->index.second, node) || checkNodeInTree(tree->index.third, node);
case TOY_AST_NODE_VAR_DECL:
return checkNodeInTree(tree->varDecl.expression, node);
case TOY_AST_NODE_FN_COLLECTION:
return checkNodeInTree(tree->fnCollection.nodes, node);
case TOY_AST_NODE_FN_DECL:
return checkNodeInTree(tree->fnDecl.arguments, node) || checkNodeInTree(tree->fnDecl.returns, node) || checkNodeInTree(tree->fnDecl.block, node);
case TOY_AST_NODE_FN_CALL:
return checkNodeInTree(tree->fnCall.arguments, node);
case TOY_AST_NODE_FN_RETURN:
return checkNodeInTree(tree->returns.returns, node);
case TOY_AST_NODE_IF:
return checkNodeInTree(tree->pathIf.condition, node) || checkNodeInTree(tree->pathIf.thenPath, node) || checkNodeInTree(tree->pathIf.elsePath, node);
case TOY_AST_NODE_WHILE:
return checkNodeInTree(tree->pathWhile.condition, node) || checkNodeInTree(tree->pathWhile.thenPath, node);
case TOY_AST_NODE_FOR:
return checkNodeInTree(tree->pathFor.preClause, node) || checkNodeInTree(tree->pathFor.condition, node) || checkNodeInTree(tree->pathFor.postClause, node) || checkNodeInTree(tree->pathFor.thenPath, node);
case TOY_AST_NODE_AND:
return checkNodeInTree(tree->pathAnd.left, node) || checkNodeInTree(tree->pathAnd.right, node);
case TOY_AST_NODE_OR:
return checkNodeInTree(tree->pathOr.left, node) || checkNodeInTree(tree->pathOr.right, node);
case TOY_AST_NODE_ERROR:
case TOY_AST_NODE_LITERAL:
case TOY_AST_NODE_BREAK:
case TOY_AST_NODE_CONTINUE:
case TOY_AST_NODE_PREFIX_INCREMENT:
case TOY_AST_NODE_PREFIX_DECREMENT:
case TOY_AST_NODE_POSTFIX_INCREMENT:
case TOY_AST_NODE_POSTFIX_DECREMENT:
case TOY_AST_NODE_IMPORT:
case TOY_AST_NODE_PASS:
return false;
}
return false;
}
//NOTE: jumpOfsets are included, because function arg and return indexes are embedded in the code body i.e. need to include their sizes in the jump
//NOTE: rootNode should NOT include groupings and blocks
static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode* node, void* breakAddressesPtr, void* continueAddressesPtr, int jumpOffsets, Toy_ASTNode* rootNode) {
@@ -302,6 +384,12 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
//special case for when indexing and assigning
if (override != TOY_OP_EOF && node->binary.opcode >= TOY_OP_VAR_ASSIGN && node->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN) {
Toy_writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
//Special case if there's an index on both sides of the sign, just set it as indexing
if (node->binary.left->type == TOY_AST_NODE_BINARY && node->binary.right->type == TOY_AST_NODE_BINARY && node->binary.left->binary.opcode == TOY_OP_INDEX && node->binary.right->binary.opcode == TOY_OP_INDEX) {
compiler->bytecode[compiler->count++] = (unsigned char)TOY_OP_INDEX;
}
compiler->bytecode[compiler->count++] = (unsigned char)TOY_OP_INDEX_ASSIGN; //1 byte WARNING: enum trickery
compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
return TOY_OP_EOF;
@@ -315,7 +403,8 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
//return this if...
Toy_Opcode ret = Toy_writeCompilerWithJumps(compiler, node->binary.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN)) { //range-based check for assignment type
//range-based check for assignment type; make sure the index is on the left of the assignment symbol
if (node->binary.opcode == TOY_OP_INDEX && rootNode->type == TOY_AST_NODE_BINARY && (rootNode->binary.opcode >= TOY_OP_VAR_ASSIGN && rootNode->binary.opcode <= TOY_OP_VAR_MODULO_ASSIGN) && !checkNodeInTree(rootNode->binary.right, node)) {
return TOY_OP_INDEX_ASSIGN_INTERMEDIATE;
}
@@ -324,9 +413,35 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
return node->binary.opcode;
}
if (ret != TOY_OP_EOF && (node->binary.opcode == TOY_OP_VAR_ASSIGN || node->binary.opcode == TOY_OP_AND || node->binary.opcode == TOY_OP_OR || (node->binary.opcode >= TOY_OP_COMPARE_EQUAL && node->binary.opcode <= TOY_OP_INVERT))) {
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
ret = TOY_OP_EOF; //untangle in this case
//untangle in these cases - (WTF, are you serious?)
if (ret != TOY_OP_EOF) {
switch(node->binary.opcode) {
case TOY_OP_NEGATE:
case TOY_OP_ADDITION:
case TOY_OP_SUBTRACTION:
case TOY_OP_MULTIPLICATION:
case TOY_OP_DIVISION:
case TOY_OP_MODULO:
case TOY_OP_VAR_ASSIGN:
case TOY_OP_VAR_ADDITION_ASSIGN:
case TOY_OP_VAR_SUBTRACTION_ASSIGN:
case TOY_OP_VAR_MULTIPLICATION_ASSIGN:
case TOY_OP_VAR_DIVISION_ASSIGN:
case TOY_OP_VAR_MODULO_ASSIGN:
case TOY_OP_COMPARE_EQUAL:
case TOY_OP_COMPARE_NOT_EQUAL:
case TOY_OP_COMPARE_LESS:
case TOY_OP_COMPARE_LESS_EQUAL:
case TOY_OP_COMPARE_GREATER:
case TOY_OP_COMPARE_GREATER_EQUAL:
case TOY_OP_INVERT:
//place the rhs result before the outer instruction
compiler->bytecode[compiler->count++] = (unsigned char)ret; //1 byte
ret = TOY_OP_EOF;
default:
break;
}
}
compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
@@ -439,7 +554,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
//write each piece of the declaration to the bytecode
int identifierIndex = Toy_findLiteralIndex(&compiler->literalCache, node->varDecl.identifier);
int identifierIndex = Toy_private_findLiteralIndex(&compiler->literalCache, node->varDecl.identifier);
if (identifierIndex < 0) {
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->varDecl.identifier);
}
@@ -472,9 +587,13 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
Toy_initCompiler(fnCompiler);
Toy_writeCompiler(fnCompiler, node->fnDecl.arguments); //can be empty, but not NULL
Toy_writeCompiler(fnCompiler, node->fnDecl.returns); //can be empty, but not NULL
Toy_Opcode override = Toy_writeCompilerWithJumps(fnCompiler, node->fnDecl.block, NULL, NULL, -4, rootNode); //can be empty, but not NULL
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
//BUGFIX: copied from TOY_AST_NODE_BLOCK, omitting the SCOPE_BEGIN and SCOPE_END opcodes (might squeeze a few bytes out of the interpreter's scopes by declaring one less)
for (int i = 0; i < node->fnDecl.block->block.count; i++) {
Toy_Opcode override = Toy_writeCompilerWithJumps(fnCompiler, &(node->fnDecl.block->block.nodes[i]), NULL, NULL, -4, &(node->fnDecl.block->block.nodes[i]));
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
fnCompiler->bytecode[fnCompiler->count++] = (unsigned char)override; //1 byte
}
}
//adopt the panic state if anything happened
@@ -483,11 +602,10 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
//create the function in the literal cache (by storing the compiler object)
Toy_Literal fnLiteral = TOY_TO_FUNCTION_LITERAL(fnCompiler, 0);
fnLiteral.type = TOY_LITERAL_FUNCTION_INTERMEDIATE; //NOTE: changing type
Toy_Literal fnLiteral = ((Toy_Literal){ .as = { .generic = fnCompiler }, .type = TOY_LITERAL_FUNCTION_INTERMEDIATE});
//push the name
int identifierIndex = Toy_findLiteralIndex(&compiler->literalCache, node->fnDecl.identifier);
int identifierIndex = Toy_private_findLiteralIndex(&compiler->literalCache, node->fnDecl.identifier);
if (identifierIndex < 0) {
identifierIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnDecl.identifier);
}
@@ -535,7 +653,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
for (int i = 0; i < node->fnCall.arguments->fnCollection.count; i++) { //reverse order, to count from the beginning in the interpreter
//sub-calls
if (node->fnCall.arguments->fnCollection.nodes[i].type != TOY_AST_NODE_LITERAL) {
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, &node->fnCall.arguments->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, &node->fnCall.arguments->fnCollection.nodes[i], breakAddressesPtr, continueAddressesPtr, jumpOffsets, node); //BUGFIX: use node as rootNode, to allow indexing within argument lists
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
@@ -543,7 +661,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
//write each argument to the bytecode
int argumentsIndex = Toy_findLiteralIndex(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
int argumentsIndex = Toy_private_findLiteralIndex(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
if (argumentsIndex < 0) {
argumentsIndex = Toy_pushLiteralArray(&compiler->literalCache, node->fnCall.arguments->fnCollection.nodes[i].atomic.literal);
}
@@ -565,7 +683,7 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
//push the argument COUNT to the top of the stack
Toy_Literal argumentsCountLiteral = TOY_TO_INTEGER_LITERAL(node->fnCall.argumentCount); //argumentCount is set elsewhere to support dot operator
int argumentsCountIndex = Toy_findLiteralIndex(&compiler->literalCache, argumentsCountLiteral);
int argumentsCountIndex = Toy_private_findLiteralIndex(&compiler->literalCache, argumentsCountLiteral);
if (argumentsCountIndex < 0) {
argumentsCountIndex = Toy_pushLiteralArray(&compiler->literalCache, argumentsCountLiteral);
}
@@ -723,12 +841,20 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
compiler->count += sizeof(unsigned short); //2 bytes
//write the body
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_BEGIN; //1 byte
bool closeScope = false;
if (node->pathFor.thenPath->type != TOY_AST_NODE_BLOCK) {
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_BEGIN; //1 byte
closeScope = true;
}
override = Toy_writeCompilerWithJumps(compiler, node->pathFor.thenPath, &breakAddresses, &continueAddresses, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_END; //1 byte
if (closeScope) {
compiler->bytecode[compiler->count++] = TOY_OP_SCOPE_END; //1 byte
}
//for-breaks actually jump to the bottom
int jumpToIncrement = compiler->count;
@@ -739,6 +865,9 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//BUGFIX: clear the stack after each loop
compiler->bytecode[compiler->count++] = TOY_OP_POP_STACK; //1 byte
compiler->bytecode[compiler->count++] = TOY_OP_JUMP; //1 byte
unsigned short tmpVal = jumpToStart + jumpOffsets;
memcpy(compiler->bytecode + compiler->count, &tmpVal, sizeof(tmpVal));
@@ -762,9 +891,6 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
memcpy(compiler->bytecode + point, &tmpVal, sizeof(tmpVal));
}
//clear the stack after use
compiler->bytecode[compiler->count++] = TOY_OP_POP_STACK; //1 byte
//cleanup
Toy_freeLiteralArray(&breakAddresses);
Toy_freeLiteralArray(&continueAddresses);
@@ -807,6 +933,54 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
}
break;
case TOY_AST_NODE_AND: {
//process the lhs
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->pathAnd.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//insert the AND opcode to signal a possible jump
compiler->bytecode[compiler->count++] = TOY_OP_AND; //1 byte
int jumpToEnd = compiler->count;
compiler->count += sizeof(unsigned short); //2 bytes
//process the rhs
override = Toy_writeCompilerWithJumps(compiler, node->pathAnd.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//set the spot to jump to, to proceed
unsigned short tmpVal = compiler->count + jumpOffsets;
memcpy(compiler->bytecode + jumpToEnd, &tmpVal, sizeof(tmpVal));
}
break;
case TOY_AST_NODE_OR: {
//process the lhs
Toy_Opcode override = Toy_writeCompilerWithJumps(compiler, node->pathOr.left, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//insert the AND opcode to signal a possible jump
compiler->bytecode[compiler->count++] = TOY_OP_OR; //1 byte
int jumpToEnd = compiler->count;
compiler->count += sizeof(unsigned short); //2 bytes
//process the rhs
override = Toy_writeCompilerWithJumps(compiler, node->pathOr.right, breakAddressesPtr, continueAddressesPtr, jumpOffsets, rootNode);
if (override != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)override; //1 byte
}
//set the spot to jump to, to proceed
unsigned short tmpVal = compiler->count + jumpOffsets;
memcpy(compiler->bytecode + jumpToEnd, &tmpVal, sizeof(tmpVal));
}
break;
case TOY_AST_NODE_FN_RETURN: {
//read each returned literal onto the stack, and return the number of values to return
for (int i = 0; i < node->returns.returns->fnCollection.count; i++) {
@@ -959,6 +1133,11 @@ static Toy_Opcode Toy_writeCompilerWithJumps(Toy_Compiler* compiler, Toy_ASTNode
return TOY_OP_INDEX_ASSIGN; //override binary's instruction IF it is assign
}
break;
case TOY_AST_NODE_PASS: {
return TOY_OP_PASS;
}
break;
}
return TOY_OP_EOF;
@@ -970,6 +1149,8 @@ void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node) {
if (op != TOY_OP_EOF) {//compensate for indexing & dot notation being screwy
compiler->bytecode[compiler->count++] = (unsigned char)op; //1 byte
}
//TODO: could free up AST Nodes
}
void Toy_freeCompiler(Toy_Compiler* compiler) {
@@ -978,6 +1159,7 @@ void Toy_freeCompiler(Toy_Compiler* compiler) {
compiler->bytecode = NULL;
compiler->capacity = 0;
compiler->count = 0;
compiler->panic = false;
}
static void emitByte(unsigned char** collationPtr, int* capacityPtr, int* countPtr, unsigned char byte) {
@@ -1025,7 +1207,7 @@ static void emitFloat(unsigned char** collationPtr, int* capacityPtr, int* count
}
//return the result
static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size, bool embedHeader) {
static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, size_t* size, bool embedHeader) {
if (compiler->panic) {
fprintf(stderr, TOY_CC_ERROR "[internal] Can't collate a panicked compiler\n" TOY_CC_RESET);
return NULL;
@@ -1096,7 +1278,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
Toy_Literal str = compiler->literalCache.literals[i];
for (int c = 0; c < TOY_AS_STRING(str)->length; c++) {
for (int c = 0; c < (int)Toy_lengthRefString(TOY_AS_STRING(str)); c++) {
emitByte(&collation, &capacity, &count, Toy_toCString(TOY_AS_STRING(str))[c]);
}
@@ -1167,17 +1349,17 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
case TOY_LITERAL_FUNCTION_INTERMEDIATE: {
//extract the compiler
Toy_Literal fn = compiler->literalCache.literals[i];
void* fnCompiler = TOY_AS_FUNCTION(fn).bytecode; //store the compiler here for now
void* fnCompiler = fn.as.generic; //store the compiler here for now
//collate the function into bytecode (without header)
int size = 0;
size_t size = 0;
unsigned char* bytes = collateCompilerHeaderOpt((Toy_Compiler*)fnCompiler, &size, false);
//emit how long this section is, +1 for ending mark
Toy_emitShort(&fnCollation, &fnCapacity, &fnCount, (unsigned short)size + 1);
//write the fn to the fn collation
for (int i = 0; i < size; i++) {
for (size_t i = 0; i < size; i++) {
emitByte(&fnCollation, &fnCapacity, &fnCount, bytes[i]);
}
@@ -1198,7 +1380,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
Toy_Literal identifier = compiler->literalCache.literals[i];
for (int c = 0; c < TOY_AS_IDENTIFIER(identifier)->length; c++) {
for (int c = 0; c < (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(identifier)); c++) {
emitByte(&collation, &capacity, &count, Toy_toCString(TOY_AS_IDENTIFIER(identifier))[c]);
}
@@ -1284,6 +1466,7 @@ static unsigned char* collateCompilerHeaderOpt(Toy_Compiler* compiler, int* size
return collation;
}
unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, int* size) {
//the whole point of the compiler is to alter bytecode, so leave it as non-const
unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, size_t* size) {
return collateCompilerHeaderOpt(compiler, size, true);
}
+44 -6
View File
@@ -1,11 +1,20 @@
#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 programs execution instructions, the flattened literals, the functions (which have their own sections and protocols within the bytecode) and version information (such as the macros defined in toy_common.h) are all combined into a single buffer of bytes, known as bytecode. This bytecode can then be safely saved to a file or immediately executed.
!*/
#include "toy_common.h"
#include "toy_opcodes.h"
#include "toy_ast_node.h"
#include "toy_literal_array.h"
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
typedef struct Toy_Compiler {
Toy_LiteralArray literalCache;
unsigned char* bytecode;
@@ -14,9 +23,38 @@ typedef struct Toy_Compiler {
bool panic;
} Toy_Compiler;
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
/*!
## Define Functions
//embed the header, data section, code section, function section, etc.
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, int* size);
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);
+47 -3
View File
@@ -1,6 +1,19 @@
#pragma once
//NOTE: you need both font AND background for these to work
/* toy_console_colors.h - console utility
This file provides a number of macros that can set the color of text in a console
window. These are used for convenience only. They are supposed to be dropped into
a printf()'s first argument, like so:
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
NOTE: you need both font AND background for these to work
*/
//platform/compiler-specific instructions
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
//fonts color
#define TOY_CC_FONT_BLACK "\033[30;"
@@ -25,6 +38,37 @@
//useful
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
#define TOY_CC_RESET "\033[0m"
#else
//fonts color
#define TOY_CC_FONT_BLACK
#define TOY_CC_FONT_RED
#define TOY_CC_FONT_GREEN
#define TOY_CC_FONT_YELLOW
#define TOY_CC_FONT_BLUE
#define TOY_CC_FONT_PURPLE
#define TOY_CC_FONT_DGREEN
#define TOY_CC_FONT_WHITE
#define TOY_CC_FONT_CYAN
//background color
#define TOY_CC_BACK_BLACK
#define TOY_CC_BACK_RED
#define TOY_CC_BACK_GREEN
#define TOY_CC_BACK_YELLOW
#define TOY_CC_BACK_BLUE
#define TOY_CC_BACK_PURPLE
#define TOY_CC_BACK_DGREEN
#define TOY_CC_BACK_WHITE
//useful
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
#define TOY_CC_RESET
#endif
+749 -714
View File
File diff suppressed because it is too large Load Diff
+165 -18
View File
@@ -1,17 +1,51 @@
#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"
typedef void (*Toy_PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions
typedef struct Toy_Interpreter {
//input
unsigned char* bytecode;
const unsigned char* bytecode;
int length;
int count;
int codeStart; //BUGFIX: for jumps, must be initialized to -1
@@ -33,21 +67,134 @@ typedef struct Toy_Interpreter {
bool panic;
} Toy_Interpreter;
//native API
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, char* name, Toy_NativeFn func);
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, char* name, Toy_HookFn hook);
/*!
## Defined Functions
!*/
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
/*!
### void Toy_initInterpreter(Toy_Interpreter* interpreter)
//utilities for the host program
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
//main access
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
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
/*!
### 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);
+24 -15
View File
@@ -12,6 +12,7 @@ static void cleanLexer(Toy_Lexer* lexer) {
lexer->start = 0;
lexer->current = 0;
lexer->line = 1;
lexer->commentsEnabled = true;
}
static bool isAtEnd(Toy_Lexer* lexer) {
@@ -54,9 +55,13 @@ static void eatWhitespace(Toy_Lexer* lexer) {
//comments
case '/':
if (!lexer->commentsEnabled) {
return;
}
//eat the line
if (peekNext(lexer) == '/') {
while (advance(lexer) != '\n' && !isAtEnd(lexer));
while (!isAtEnd(lexer) && advance(lexer) != '\n');
break;
}
@@ -64,7 +69,7 @@ static void eatWhitespace(Toy_Lexer* lexer) {
if (peekNext(lexer) == '*') {
advance(lexer);
advance(lexer);
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
while(!isAtEnd(lexer) && !(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
advance(lexer);
advance(lexer);
break;
@@ -112,7 +117,7 @@ static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("err:");
Toy_printToken(&token);
Toy_private_printToken(&token);
}
#endif
@@ -131,7 +136,7 @@ static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
//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_printToken(&token);
Toy_private_printToken(&token);
}
#endif
@@ -163,7 +168,7 @@ static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
} else {
printf("flt:");
}
Toy_printToken(&token);
Toy_private_printToken(&token);
}
#endif
@@ -216,7 +221,7 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("str:");
Toy_printToken(&token);
Toy_private_printToken(&token);
}
#endif
@@ -232,7 +237,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
//scan for a keyword
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
if (strlen(Toy_keywordTypes[i].keyword) == (size_t)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
Toy_Token token;
token.type = Toy_keywordTypes[i].type;
@@ -243,7 +248,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("kwd:");
Toy_printToken(&token);
Toy_private_printToken(&token);
}
#endif
@@ -262,7 +267,7 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("idf:");
Toy_printToken(&token);
Toy_private_printToken(&token);
}
#endif
@@ -270,13 +275,13 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
}
//exposed functions
void Toy_initLexer(Toy_Lexer* lexer, char* source) {
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
cleanLexer(lexer);
lexer->source = source;
}
Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
eatWhitespace(lexer);
lexer->start = lexer->current;
@@ -312,10 +317,10 @@ Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
} else {
return makeToken(lexer, TOY_TOKEN_AND);
return makeToken(lexer, TOY_TOKEN_AND_AND);
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR_OR : TOY_TOKEN_PIPE);
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
@@ -347,7 +352,7 @@ static void trim(char** s, int* l) { //all this to remove a newline?
}
//for debugging
void Toy_printToken(Toy_Token* token) {
void Toy_private_printToken(Toy_Token* token) {
if (token->type == TOY_TOKEN_ERROR) {
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
return;
@@ -363,7 +368,7 @@ void Toy_printToken(Toy_Token* token) {
if (keyword != NULL) {
printf("%s", keyword);
} else {
char* str = token->lexeme;
char* str = (char*)token->lexeme; //strip const-ness for trimming
int length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
@@ -372,3 +377,7 @@ void Toy_printToken(Toy_Token* token) {
printf("\n");
}
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
lexer->commentsEnabled = enabled;
}
+45 -6
View File
@@ -1,26 +1,65 @@
#pragma once
/*!
# toy_lexer.h
This header defines the lexer and token structures, which can be bound to a piece of source code, and used to tokenize it within a parser.
!*/
#include "toy_common.h"
#include "toy_token_types.h"
//lexers are bound to a string of code, and return a single token every time scan is called
typedef struct {
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;
//tokens are intermediaries between lexers and parsers
typedef struct {
Toy_TokenType type;
char* lexeme;
const char* lexeme;
int length;
int line;
} Toy_Token;
TOY_API void Toy_initLexer(Toy_Lexer* lexer, char* source);
Toy_Token Toy_scanLexer(Toy_Lexer* lexer);
/*!
## Defined Functions
!*/
//for debugging
void Toy_printToken(Toy_Token* token);
/*!
### void Toy_initLexer(Toy_Lexer* lexer, const char* source)
This function initializes a lexer, binding it to the `source` parameter; the lexer is now ready to be passed to the parser.
!*/
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
/*!
### Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer)
This function "scans" the lexer, returning a token to the parser.
Private functions are not intended for general use.
!*/
TOY_API Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
/*!
### void Toy_private_printToken(Toy_Token* token)
This function prints a given token to stdout.
Private functions are not intended for general use.
!*/
TOY_API void Toy_private_printToken(Toy_Token* token);
/*!
### void Toy_private_setComments(Toy_Lexer* lexer, bool enabled)
This function sets whether comments are allowed within source code. By default, comments are allowed, and are only disabled in the repl.
Private functions are not intended for general use.
!*/
TOY_API void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
+39 -28
View File
@@ -8,6 +8,7 @@
#include "toy_console_colors.h"
#include <stdio.h>
#include <string.h>
//hash util functions
static unsigned int hashString(const char* string, int length) {
@@ -58,10 +59,10 @@ void Toy_freeLiteral(Toy_Literal literal) {
if (TOY_IS_FUNCTION(literal)) {
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
TOY_AS_FUNCTION(literal).scope = NULL;
TOY_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).bytecode, TOY_AS_FUNCTION(literal).length);
Toy_deleteRefFunction((Toy_RefFunction*)(TOY_AS_FUNCTION(literal).inner.ptr));
}
if (TOY_IS_TYPE(literal)) {
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]);
}
@@ -72,7 +73,7 @@ void Toy_freeLiteral(Toy_Literal literal) {
bool Toy_private_isTruthy(Toy_Literal x) {
if (TOY_IS_NULL(x)) {
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Null is neither true nor false\n" TOY_CC_RESET);
fprintf(stderr, TOY_CC_ERROR "Null is neither true nor false\n" TOY_CC_RESET);
return false;
}
@@ -83,12 +84,8 @@ bool Toy_private_isTruthy(Toy_Literal x) {
return true;
}
Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){TOY_LITERAL_STRING, { .string.ptr = ptr }});
}
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){TOY_LITERAL_IDENTIFIER,{ .identifier.ptr = ptr, .identifier.hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(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) {
@@ -122,6 +119,10 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
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]);
@@ -134,6 +135,15 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
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) ) {
@@ -145,17 +155,16 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
}
case TOY_LITERAL_FUNCTION: {
unsigned char* buffer = TOY_ALLOCATE(unsigned char, TOY_AS_FUNCTION(original).length);
memcpy(buffer, TOY_AS_FUNCTION(original).bytecode, TOY_AS_FUNCTION(original).length);
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(Toy_copyRefFunction( TOY_AS_FUNCTION(original).inner.ptr ));
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(buffer, TOY_AS_FUNCTION(original).length);
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
return literal;
}
case TOY_LITERAL_IDENTIFIER: {
return TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_IDENTIFIER(original)));
//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: {
@@ -172,7 +181,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return original; //literally a shallow copy
}
case TOY_LITERAL_ARRAY_INTERMEDIATE: {
case TOY_LITERAL_ARRAY_INTERMEDIATE: { //TODO: efficient preallocation?
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
@@ -188,7 +197,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return ret;
}
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: { //TODO: efficient preallocation?
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
@@ -204,7 +213,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return ret;
}
case TOY_LITERAL_TYPE_INTERMEDIATE: {
case TOY_LITERAL_TYPE_INTERMEDIATE: { //TODO: efficient preallocation?
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
@@ -228,7 +237,7 @@ Toy_Literal Toy_copyLiteral(Toy_Literal original) {
return original;
default:
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
fprintf(stderr, TOY_CC_ERROR "Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
return TOY_TO_NULL_LITERAL;
}
}
@@ -239,10 +248,10 @@ bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
// 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);
return TOY_AS_INTEGER(lhs) == TOY_AS_FLOAT(rhs);
}
else {
return TOY_AS_FLOAT(lhs) + TOY_AS_INTEGER(rhs);
return TOY_AS_FLOAT(lhs) == TOY_AS_INTEGER(rhs);
}
}
@@ -376,8 +385,10 @@ int Toy_hashLiteral(Toy_Literal lit) {
case TOY_LITERAL_INTEGER:
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
case TOY_LITERAL_FLOAT:
return hashUInt(*(unsigned int*)(&TOY_AS_FLOAT(lit)));
case TOY_LITERAL_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)));
@@ -404,20 +415,20 @@ int Toy_hashLiteral(Toy_Literal lit) {
case TOY_LITERAL_FUNCTION:
case TOY_LITERAL_FUNCTION_NATIVE:
case TOY_LITERAL_FUNCTION_HOOK:
return 0; //can't hash these
return -1; //can't hash these
case TOY_LITERAL_IDENTIFIER:
return TOY_HASH_I(lit); //pre-computed
case TOY_LITERAL_TYPE:
return TOY_AS_TYPE(lit).typeOf; //nothing else I can do
return -1; //not much i can really do
case TOY_LITERAL_OPAQUE:
case TOY_LITERAL_ANY:
return -1;
default:
//should never bee seen
//should never be seen
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
return 0;
}
@@ -444,7 +455,7 @@ static void printToBuffer(const char* str) {
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str ? str : "\0");
globalPrintCount += strlen(str);
}
@@ -453,7 +464,7 @@ void Toy_printLiteral(Toy_Literal literal) {
Toy_printLiteralCustom(literal, stdoutWrapper);
}
void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
void Toy_printLiteralCustom(Toy_Literal literal, Toy_PrintFn printFn) {
switch(literal.type) {
case TOY_LITERAL_NULL:
printFn("null");
@@ -487,10 +498,10 @@ void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
case TOY_LITERAL_STRING: {
char buffer[TOY_MAX_STRING_LENGTH];
if (!quotes) {
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
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, Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
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);
}
@@ -596,7 +607,7 @@ void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
case TOY_LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
snprintf(buffer, 256, "%.*s", (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
printFn(buffer);
}
break;
+284 -53
View File
@@ -1,17 +1,54 @@
#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*);
#include <string.h>
/*!
## 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,
@@ -39,47 +76,76 @@ typedef enum {
} Toy_LiteralType;
typedef struct Toy_Literal {
Toy_LiteralType type;
union {
bool boolean;
int integer;
float number;
bool boolean; //1
int integer; //4
float number;//4
struct {
Toy_RefString* ptr;
Toy_RefString* ptr; //8
//string hash?
} string;
} string; //8
void* array;
void* dictionary;
struct Toy_LiteralArray* array; //8
struct Toy_LiteralDictionary* dictionary; //8
struct {
void* bytecode;
Toy_NativeFn native; //already a pointer
Toy_HookFn hook; //already a pointer
void* scope;
int length;
} function;
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;
int hash;
} identifier;
Toy_RefString* ptr; //8
int hash; //4
} identifier; //16
struct {
Toy_LiteralType typeOf;
bool constant;
void* subtypes; //for nested types caused by compounds
int capacity;
int count;
} type;
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;
int tag;
} opaque;
} as;
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)
@@ -94,6 +160,23 @@ typedef struct Toy_Literal {
#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)
@@ -101,49 +184,197 @@ typedef struct Toy_Literal {
#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.native)
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.hook)
#define TOY_AS_FUNCTION_NATIVE(value) ((value).as.function.inner.native)
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.inner.hook)
#define TOY_AS_IDENTIFIER(value) ((value).as.identifier.ptr)
#define TOY_AS_TYPE(value) ((value).as.type)
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
#define TOY_TO_NULL_LITERAL ((Toy_Literal){TOY_LITERAL_NULL, { .integer = 0 }})
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){TOY_LITERAL_BOOLEAN, { .boolean = value }})
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){TOY_LITERAL_INTEGER, { .integer = value }})
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FLOAT, { .number = value }})
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_ARRAY, { .array = value }})
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_DICTIONARY, { .dictionary = value }})
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){TOY_LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FUNCTION_NATIVE, { .function.native = value, .function.scope = NULL, .function.length = 0 }})
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FUNCTION_HOOK, { .function.hook = value, .function.scope = NULL, .function.length = 0 }})
/*!
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){ TOY_LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){ TOY_LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
#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
//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){TOY_LITERAL_INDEX_BLANK, { .integer = 0 }})
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){{ .integer = 0 }, TOY_LITERAL_INDEX_BLANK})
TOY_API void Toy_freeLiteral(Toy_Literal literal);
/*!
## More Defined Macros
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
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
//BUGFIX: macros are not functions
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
TOY_API Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
/*!
## Defined Functions
!*/
//utils
/*!
### 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);
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*));
/*!
### 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);
+5 -3
View File
@@ -18,8 +18,10 @@ void Toy_freeLiteralArray(Toy_LiteralArray* array) {
Toy_freeLiteral(array->literals[i]);
}
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
Toy_initLiteralArray(array);
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) {
@@ -50,7 +52,7 @@ Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
}
//find a literal in the array that matches the "literal" argument
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
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) {
+63 -1
View File
@@ -1,5 +1,13 @@
#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"
@@ -10,11 +18,65 @@ typedef struct Toy_LiteralArray {
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_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
/*!
### 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
+41 -29
View File
@@ -7,7 +7,7 @@
#include <stdio.h>
//util functions
static void setEntryValues(Toy_private_entry* entry, Toy_Literal key, Toy_Literal value) {
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);
@@ -16,17 +16,23 @@ static void setEntryValues(Toy_private_entry* entry, Toy_Literal key, Toy_Litera
entry->value = Toy_copyLiteral(value);
}
static Toy_private_entry* getEntryArray(Toy_private_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
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
unsigned int index = hash % capacity;
unsigned int start = index;
int index = hash % capacity;
int start = index;
//increment once, so it can't equal start
index = (index + 1) % capacity;
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_entry* entry = &array[index];
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) {
@@ -40,15 +46,18 @@ static Toy_private_entry* getEntryArray(Toy_private_entry* array, int capacity,
}
}
index = (index + 1) % capacity;
if (++index >= capacity) {
index = 0;
}
//index = (index + 1) % capacity;
}
return NULL;
}
static void adjustEntryCapacity(Toy_private_entry** dictionaryHandle, int oldCapacity, int capacity) {
static void adjustEntryCapacity(Toy_private_dictionary_entry** dictionaryHandle, int oldCapacity, int capacity) {
//new entry space
Toy_private_entry* newEntries = TOY_ALLOCATE(Toy_private_entry, capacity);
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;
@@ -62,19 +71,21 @@ static void adjustEntryCapacity(Toy_private_entry** dictionaryHandle, int oldCap
}
//place the key and value in the new array (reusing string memory)
Toy_private_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
Toy_private_dictionary_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
entry->key = (*dictionaryHandle)[i].key;
entry->value = (*dictionaryHandle)[i].value;
}
//clear the old array
TOY_FREE_ARRAY(Toy_private_entry, *dictionaryHandle, oldCapacity);
if (oldCapacity > 0) {
TOY_FREE_ARRAY(Toy_private_dictionary_entry, *dictionaryHandle, oldCapacity);
}
*dictionaryHandle = newEntries;
}
static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
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;
@@ -82,7 +93,7 @@ static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
}
Toy_private_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
Toy_private_dictionary_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
//true = contains increase
if (TOY_IS_NULL(entry->key)) {
@@ -97,14 +108,14 @@ static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr
return false;
}
static void freeEntry(Toy_private_entry* entry) {
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_entry* array, int capacity) {
static void freeEntryArray(Toy_private_dictionary_entry* array, int capacity) {
if (array == NULL) {
return;
}
@@ -115,23 +126,24 @@ static void freeEntryArray(Toy_private_entry* array, int capacity) {
}
}
TOY_FREE_ARRAY(Toy_private_entry, array, capacity);
TOY_FREE_ARRAY(Toy_private_dictionary_entry, array, capacity);
}
//exposed functions
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = TOY_GROW_CAPACITY(0);
dictionary->capacity = 0;
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
dictionary->capacity = 0;
}
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
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) {
@@ -141,7 +153,7 @@ void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key
}
//BUGFIX: Can't hash a function
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
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;
}
@@ -166,7 +178,7 @@ Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Lite
}
//BUGFIX: Can't hash a function
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
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;
}
@@ -176,7 +188,7 @@ Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Lite
return TOY_TO_NULL_LITERAL;
}
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
if (entry != NULL) {
return Toy_copyLiteral(entry->value);
@@ -193,7 +205,7 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
}
//BUGFIX: Can't hash a function
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
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;
}
@@ -203,7 +215,7 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
return;
}
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
Toy_private_dictionary_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
if (entry != NULL) {
freeEntry(entry);
@@ -214,6 +226,6 @@ void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
//null & not tombstoned
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
return !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
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));
}
+70 -3
View File
@@ -1,29 +1,96 @@
#pragma once
/*!
# toy_literal_dictionary.h
This header defines the dictionary structure (as well as the private entry structure), which manages a series of `Toy_Literal` instances stored in a key-value hash map. The dictionary does not take ownership of given literals, instead it makes an internal copy.
The dictionary type is one of two fundemental data structures used throughout Toy - the other is the array.
!*/
#include "toy_common.h"
#include "toy_literal.h"
/*!
## Defined Macros
!*/
/*!
### TOY_DICTIONARY_MAX_LOAD
If the contents of a dictionary exceeds this percentage of it's capacity, then a new buffer is created, the old contents are copied over one-by-one, and the original buffer is freed.
Since this process can be memory and time intensive, a configurable macro is used to allow for fine-grained control across the lang.
The current default value is `0.75`, representing 75% capacity.
!*/
//TODO: benchmark this
#define TOY_DICTIONARY_MAX_LOAD 0.75
typedef struct Toy_private_entry {
typedef struct Toy_private_dictionary_entry {
Toy_Literal key;
Toy_Literal value;
} Toy_private_entry;
} Toy_private_dictionary_entry;
typedef struct Toy_LiteralDictionary {
Toy_private_entry* entries;
Toy_private_dictionary_entry* entries;
int capacity;
int count;
int contains; //count + tombstones, for internal use
} Toy_LiteralDictionary;
/*!
## Defined Functions
!*/
/*!
### void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary)
This function initializes the `Toy_LiteralDictionary` pointed to by `dictionary`.
!*/
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
/*!
### void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary)
This function frees a `Toy_LiteralDictionary` pointed to by `dictionary`. Every literal within is passed to `Toy_freeLiteral()` before its memory is released.
!*/
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
/*!
### void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value)
This function inserts the given key-value pair of literals into `dictionary`, creating it if it doesn't exist, or freeing and overwriting it if `key` is already present. This function may also expand the memory buffer if needed.
When expanding the memory buffer, a full copy of the existing dictionary's contents is created - this can be memory intensive.
Literal functions and opaques cannot be used as keys.
!*/
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
/*!
### Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
This function returns the value of the literal within `dictionary` identified by `key`, or a null literal if it doesn't exist.
Literal functions and opaques cannot be used as keys.
!*/
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
/*!
### void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
This function removes the key-value pair of literals from `dictionary` identified by `key`, if it exists.
Literal functions and opaques cannot be used as keys.
!*/
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
/*!
### bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key)
This function returns true if the key-value pair identified by `key` exists within `dictionary`, otherwise it returns false.
!*/
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
+8 -7
View File
@@ -1,5 +1,6 @@
#include "toy_memory.h"
#include "toy_refstring.h"
#include "toy_reffunction.h"
#include "toy_console_colors.h"
@@ -8,22 +9,21 @@
//default allocator
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
if (newSize == 0 && oldSize == 0) {
//causes issues, so just skip out with a NO-OP
return NULL;
}
//causes issues, so just skip out with a NO-OP (DISABLED for performance reasons)
// if (newSize == 0 && oldSize == 0) {
// return NULL;
// }
if (newSize == 0) {
free(pointer);
return NULL;
}
void* mem = realloc(pointer, newSize);
if (mem == NULL) {
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
exit(-1);
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %zu, replacing %zu)\n" TOY_CC_RESET, newSize, oldSize);
return NULL;
}
return mem;
@@ -50,4 +50,5 @@ void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
allocator = fn;
Toy_setRefStringAllocatorFn(fn);
Toy_setRefFunctionAllocatorFn(fn);
}
+104 -10
View File
@@ -1,18 +1,112 @@
#pragma once
/*!
# toy_memory.h
This header defines all of the memory management utilities. Any and all heap-based memory management goes through these utilities.
A default memory allocator function is used internally, but it can be overwritten for diagnostic and platform related purposes.
!*/
#include "toy_common.h"
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
/*!
## Defined Macros
!*/
//implementation details
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
/*!
### TOY_GROW_CAPACITY(capacity)
This macro calculates, in place, what size of memory should be allocated based on the previous size.
!*/
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
/*!
### TOY_GROW_CAPACITY_FAST(capacity)
This macro calculates, in place, what size of memory should be allocated based on the previous size. It grows faster than `TOY_GROW_CAPACITY`.
!*/
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
/*
### TOY_ALLOCATE(type, count)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be allocated, and `count` is the number which will be needed (usually calculated with `TOY_GROW_CAPACITY`).
This returns a pointer of `type`.
*/
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
/*!
### TOY_FREE(type, pointer)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be freed, and `pointer` is to what is being freed. This should only be used when a single element has been allocated, as opposed to an array.
!*/
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
/*!
### TOY_FREE_ARRAY(type, pointer, oldCount)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that will be freed, `pointer` is a reference to what is being freed, and `oldCount` is the size of the array being freed. This should only be used when an array has been allocated, as opposed to a single element.
!*/
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
/*!
### TOY_GROW_ARRAY(type, pointer, oldCount, count)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that is being operated on, `pointer` is what is being resized, `oldCount` is the previous size of the array and `count` is the new size of the array (usually calculated with `TOY_GROW_CAPACITY`).
This returns a pointer of `type`.
!*/
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
/*!
### TOY_SHRINK_ARRAY(type, pointer, oldCount, count)
This macro wraps `Toy_reallocate()`, which itself calls the allocator function. `type` is the type that is being operated on, `pointer` is what is being resized, `oldCount` is the previous size of the array and `count` is the new size of the array.
This returns a pointer of `type`.
!*/
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
/*!
## Defined Interfaces
!*/
/*!
### typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
This function interface is used for defining any memory allocator functions.
Any and all memory allocator functions should:
* Take a `pointer` to a previously allocated block of memory, or `NULL`
* Take the `oldSize`, which is the previous size of the `pointer` allocated, in bytes (`oldSize` can be 0)
* Take the `newSize`, which is the new size of the buffer to be allocaated, in bytes (`newSize` can be 0)
* Return the newly allocated buffer, or `NULL` if `newSize` is zero
* Return `NULL` on error
!*/
//assign the memory allocator
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
## Defined Functions
!*/
/*!
### TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize)
This function shouldn't be called directly. Instead, use one of the given macros.
This function wraps a call to the internal assigned memory allocator.
!*/
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
/*!
### void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn)
This function sets the memory allocator, replacing the default memory allocator.
This function also overwrites any given refstring and reffunction memory allocators, see [toy_refstring.h](toy_refstring_h.md).
!*/
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
+9 -2
View File
@@ -3,6 +3,9 @@
typedef enum Toy_Opcode {
TOY_OP_EOF,
//do nothing
TOY_OP_PASS,
//basic statements
TOY_OP_ASSERT,
TOY_OP_PRINT,
@@ -26,8 +29,8 @@ typedef enum Toy_Opcode {
TOY_OP_SCOPE_BEGIN,
TOY_OP_SCOPE_END,
TOY_OP_TYPE_DECL, //declare a type to be used (as a literal)
TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
TOY_OP_TYPE_DECL_removed,
TOY_OP_TYPE_DECL_LONG_removed,
TOY_OP_VAR_DECL, //declare a variable to be used (as a literal)
TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
@@ -83,5 +86,9 @@ typedef enum Toy_Opcode {
TOY_OP_FN_END, //different from SECTION_END
TOY_OP_SECTION_END = 255,
//TODO: add more
//prefix & postfix signals (used internally)
TOY_OP_PREFIX,
TOY_OP_POSTFIX,
} Toy_Opcode;
+167 -48
View File
@@ -32,7 +32,7 @@ static void error(Toy_Parser* parser, Toy_Token token, const char* message) {
static void advance(Toy_Parser* parser) {
parser->previous = parser->current;
parser->current = Toy_scanLexer(parser->lexer);
parser->current = Toy_private_scanLexer(parser->lexer);
if (parser->current.type == TOY_TOKEN_ERROR) {
error(parser, parser->current, "Toy_Lexer error");
@@ -119,6 +119,7 @@ ParseRule parseRules[];
static void declaration(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, PrecedenceRule rule);
static Toy_Literal readTypeToLiteral(Toy_Parser* parser);
static void varDecl(Toy_Parser* parser, Toy_ASTNode** nodeHandle);
//TODO: resolve the messy order of these
//the expression rules
@@ -140,7 +141,7 @@ static Toy_Opcode asType(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
static Toy_Opcode typeOf(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
Toy_ASTNode* rhs = NULL;
parsePrecedence(parser, &rhs, PREC_TERNARY);
parsePrecedence(parser, &rhs, PREC_CALL);
Toy_emitASTNodeUnary(nodeHandle, TOY_OP_TYPE_OF, rhs);
return TOY_OP_EOF;
}
@@ -167,6 +168,10 @@ static Toy_Opcode compound(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
consume(parser, TOY_TOKEN_COMMA, "Expected ',' in array or dictionary");
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) { //allow for trailing commas
break;
}
iterations++;
Toy_ASTNode* left = NULL;
@@ -334,6 +339,28 @@ static Toy_Opcode grouping(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
}
}
static Toy_Opcode circuit(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
//handle short-circuitable operators - && ||
switch (parser->previous.type) {
case TOY_TOKEN_AND_AND: {
parsePrecedence(parser, nodeHandle, PREC_AND + 1);
return TOY_OP_AND;
}
case TOY_TOKEN_OR_OR: {
parsePrecedence(parser, nodeHandle, PREC_OR + 1);
return TOY_OP_OR;
}
default: {
error(parser, parser->previous, "Unexpected token passed to grouping precedence rule");
return TOY_OP_EOF;
}
}
}
static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
@@ -341,102 +368,92 @@ static Toy_Opcode binary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
switch(parser->previous.type) {
//arithmetic
case TOY_TOKEN_PLUS: {
parsePrecedence(parser, nodeHandle, PREC_TERM);
parsePrecedence(parser, nodeHandle, PREC_TERM + 1);
return TOY_OP_ADDITION;
}
case TOY_TOKEN_MINUS: {
parsePrecedence(parser, nodeHandle, PREC_TERM);
parsePrecedence(parser, nodeHandle, PREC_TERM + 1);
return TOY_OP_SUBTRACTION;
}
case TOY_TOKEN_MULTIPLY: {
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
return TOY_OP_MULTIPLICATION;
}
case TOY_TOKEN_DIVIDE: {
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
return TOY_OP_DIVISION;
}
case TOY_TOKEN_MODULO: {
parsePrecedence(parser, nodeHandle, PREC_FACTOR);
parsePrecedence(parser, nodeHandle, PREC_FACTOR + 1);
return TOY_OP_MODULO;
}
//assignment
case TOY_TOKEN_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_ASSIGN;
}
case TOY_TOKEN_PLUS_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_ADDITION_ASSIGN;
}
case TOY_TOKEN_MINUS_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_SUBTRACTION_ASSIGN;
}
case TOY_TOKEN_MULTIPLY_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_MULTIPLICATION_ASSIGN;
}
case TOY_TOKEN_DIVIDE_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_DIVISION_ASSIGN;
}
case TOY_TOKEN_MODULO_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT + 1);
return TOY_OP_VAR_MODULO_ASSIGN;
}
//comparison
case TOY_TOKEN_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_EQUAL;
}
case TOY_TOKEN_NOT_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_NOT_EQUAL;
}
case TOY_TOKEN_LESS: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_LESS;
}
case TOY_TOKEN_LESS_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_LESS_EQUAL;
}
case TOY_TOKEN_GREATER: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_GREATER;
}
case TOY_TOKEN_GREATER_EQUAL: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
parsePrecedence(parser, nodeHandle, PREC_COMPARISON + 1);
return TOY_OP_COMPARE_GREATER_EQUAL;
}
case TOY_TOKEN_AND: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
return TOY_OP_AND;
}
case TOY_TOKEN_OR: {
parsePrecedence(parser, nodeHandle, PREC_COMPARISON);
return TOY_OP_OR;
}
default:
error(parser, parser->previous, "Unexpected token passed to binary precedence rule");
return TOY_OP_EOF;
@@ -448,7 +465,7 @@ static Toy_Opcode unary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
if (parser->previous.type == TOY_TOKEN_MINUS) {
//temp handle to potentially negate values
parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal
parsePrecedence(parser, &tmpNode, PREC_TERM); //can be a literal
//optimisation: check for negative literals
if (tmpNode != NULL && tmpNode->type == TOY_AST_NODE_LITERAL && (TOY_IS_INTEGER(tmpNode->atomic.literal) || TOY_IS_FLOAT(tmpNode->atomic.literal))) {
@@ -508,7 +525,7 @@ static Toy_Opcode unary(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
return TOY_OP_EOF;
}
static char* removeChar(char* lexeme, int length, char c) {
static char* removeChar(const char* lexeme, int length, char c) {
int resPos = 0;
char* result = TOY_ALLOCATE(char, length + 1);
@@ -540,7 +557,7 @@ static Toy_Opcode atomic(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
case TOY_TOKEN_LITERAL_INTEGER: {
int value = 0;
char* lexeme = removeChar(parser->previous.lexeme, parser->previous.length, '_');
const char* lexeme = removeChar(parser->previous.lexeme, parser->previous.length, '_');
sscanf(lexeme, "%d", &value);
TOY_FREE_ARRAY(char, lexeme, parser->previous.length + 1);
Toy_emitASTNodeLiteral(nodeHandle, TOY_TO_INTEGER_LITERAL(value));
@@ -549,7 +566,7 @@ static Toy_Opcode atomic(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
case TOY_TOKEN_LITERAL_FLOAT: {
float value = 0;
char* lexeme = removeChar(parser->previous.lexeme, parser->previous.length, '_');
const char* lexeme = removeChar(parser->previous.lexeme, parser->previous.length, '_');
sscanf(lexeme, "%f", &value);
TOY_FREE_ARRAY(char, lexeme, parser->previous.length + 1);
Toy_emitASTNodeLiteral(nodeHandle, TOY_TO_FLOAT_LITERAL(value));
@@ -627,6 +644,14 @@ static Toy_Opcode castingPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
}
break;
//BUGFIX: handle this here, and not in castingPrefix, so "any" can be recognized as a type properly
case TOY_TOKEN_ANY: {
Toy_Literal literal = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, false);
Toy_emitASTNodeLiteral(nodeHandle, literal);
Toy_freeLiteral(literal);
}
break;
default:
error(parser, parser->previous, "Unexpected token passed to casting precedence rule");
return TOY_OP_EOF;
@@ -658,7 +683,7 @@ static Toy_Opcode castingInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
break;
case TOY_TOKEN_LITERAL_STRING:
atomic(parser, nodeHandle);
string(parser, nodeHandle);
break;
default:
@@ -676,11 +701,15 @@ static Toy_Opcode incrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle)
Toy_ASTNode* tmpNode = NULL;
identifier(parser, &tmpNode);
if (!tmpNode) {
return TOY_OP_EOF;
}
Toy_emitASTNodePrefixIncrement(nodeHandle, tmpNode->atomic.literal);
Toy_freeASTNode(tmpNode);
return TOY_OP_EOF;
return TOY_OP_PREFIX;
}
static Toy_Opcode incrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
@@ -689,24 +718,32 @@ static Toy_Opcode incrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
if (!tmpNode) {
return TOY_OP_EOF;
}
Toy_emitASTNodePostfixIncrement(nodeHandle, tmpNode->atomic.literal);
Toy_freeASTNode(tmpNode);
return TOY_OP_EOF;
return TOY_OP_POSTFIX;
}
static Toy_Opcode decrementPrefix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
Toy_ASTNode* tmpNode = NULL;
identifier(parser, &tmpNode); //weird
identifier(parser, &tmpNode);
if (!tmpNode) {
return TOY_OP_EOF;
}
Toy_emitASTNodePrefixDecrement(nodeHandle, tmpNode->atomic.literal);
Toy_freeASTNode(tmpNode);
return TOY_OP_EOF;
return TOY_OP_PREFIX;
}
static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
@@ -715,14 +752,26 @@ static Toy_Opcode decrementInfix(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
advance(parser);
if (!tmpNode) {
return TOY_OP_EOF;
}
Toy_emitASTNodePostfixDecrement(nodeHandle, tmpNode->atomic.literal);
Toy_freeASTNode(tmpNode);
return TOY_OP_EOF;
return TOY_OP_POSTFIX;
}
static Toy_Opcode fnCall(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//wait - is the previous token a type? this should be casting instead
if (parser->previous.type >= TOY_TOKEN_NULL && parser->previous.type <= TOY_TOKEN_ANY) {
//casting value
parsePrecedence(parser, nodeHandle, PREC_CALL);
return TOY_OP_TYPE_CAST; //opcode value
}
advance(parser); //skip the left paren
//binary() is an infix rule - so only get the RHS of the operator
@@ -794,13 +843,20 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
//eat the first
if (!match(parser, TOY_TOKEN_COLON)) {
Toy_freeASTNode(first);
first = NULL;
parsePrecedence(parser, &first, PREC_TERNARY);
match(parser, TOY_TOKEN_COLON);
readFirst = true;
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
if (!first) {
Toy_freeASTNode(first);
Toy_freeASTNode(second);
Toy_freeASTNode(third);
return TOY_OP_EOF;
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
if (readFirst) {
Toy_freeASTNode(second);
second = NULL;
@@ -816,10 +872,18 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
//eat the second
if (!match(parser, TOY_TOKEN_COLON)) {
Toy_freeASTNode(second);
second = NULL;
parsePrecedence(parser, &second, PREC_TERNARY);
match(parser, TOY_TOKEN_COLON);
}
if (!second) {
Toy_freeASTNode(first);
Toy_freeASTNode(second);
Toy_freeASTNode(third);
return TOY_OP_EOF;
}
if (match(parser, TOY_TOKEN_BRACKET_RIGHT)) {
Toy_freeASTNode(third);
third = NULL;
@@ -829,7 +893,16 @@ static Toy_Opcode indexAccess(Toy_Parser* parser, Toy_ASTNode** nodeHandle) { //
//eat the third
Toy_freeASTNode(third);
third = NULL;
parsePrecedence(parser, &third, PREC_TERNARY);
if (!third) {
Toy_freeASTNode(first);
Toy_freeASTNode(second);
Toy_freeASTNode(third);
return TOY_OP_EOF;
}
Toy_emitASTNodeIndex(nodeHandle, first, second, third);
consume(parser, TOY_TOKEN_BRACKET_RIGHT, "Expected ']' in index notation");
@@ -878,7 +951,7 @@ ParseRule parseRules[] = { //must match the token types
{NULL, NULL, PREC_NONE},// TOKEN_DICTIONARY,
{NULL, NULL, PREC_NONE},// TOKEN_FUNCTION,
{NULL, NULL, PREC_NONE},// TOKEN_OPAQUE,
{NULL, NULL, PREC_NONE},// TOKEN_ANY,
{castingPrefix, NULL, PREC_CALL},// TOKEN_ANY,
//keywords and reserved words
{NULL, NULL, PREC_NONE},// TOKEN_AS,
@@ -941,8 +1014,8 @@ ParseRule parseRules[] = { //must match the token types
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER,
{NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL,
{NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL,
{NULL, binary, PREC_AND},// TOKEN_AND,
{NULL, binary, PREC_OR},// TOKEN_OR,
{NULL, circuit, PREC_AND},// TOKEN_AND,
{NULL, circuit, PREC_OR},// TOKEN_OR,
//other operators
{NULL, question, PREC_TERNARY}, //TOKEN_QUESTION,
@@ -1217,6 +1290,23 @@ static void parsePrecedence(Toy_Parser* parser, Toy_ASTNode** nodeHandle, Preced
continue;
}
//BUGFIX: keep going, don't skip out on a postfix
if (opcode == TOY_OP_PREFIX || opcode == TOY_OP_POSTFIX) {
Toy_freeASTNode(*nodeHandle);
*nodeHandle = rhsNode;
continue;
}
if (opcode == TOY_OP_AND) {
Toy_emitASTNodeAnd(nodeHandle, rhsNode);
continue;
}
if (opcode == TOY_OP_OR) {
Toy_emitASTNodeOr(nodeHandle, rhsNode);
continue;
}
Toy_emitASTNodeBinary(nodeHandle, rhsNode, opcode);
//optimise away the constants
@@ -1334,13 +1424,42 @@ static void forStmt(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
//read the clauses
consume(parser, TOY_TOKEN_PAREN_LEFT, "Expected '(' at beginning of for clause");
declaration(parser, &preClause); //allow defining variables in the pre-clause
//check the pre-clause
if (parser->current.type != TOY_TOKEN_SEMICOLON) {
//allow defining variables in the pre-clause
if (match(parser, TOY_TOKEN_VAR)) {
varDecl(parser, &preClause);
}
else {
parsePrecedence(parser, &preClause, PREC_ASSIGNMENT);
}
}
else {
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after empty declaration of for clause");
Toy_emitASTNodePass(&preClause);
}
parsePrecedence(parser, &condition, PREC_TERNARY);
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after condition of for clause");
//check the condition clause
if (parser->current.type != TOY_TOKEN_SEMICOLON) {
parsePrecedence(parser, &condition, PREC_TERNARY);
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after condition of for clause");
}
else {
consume(parser, TOY_TOKEN_SEMICOLON, "Expected ';' after empty condition of for clause");
//empty clause defaults to forever
Toy_Literal f = TOY_TO_BOOLEAN_LITERAL(true);
Toy_emitASTNodeLiteral(&condition, f);
}
parsePrecedence(parser, &postClause, PREC_ASSIGNMENT);
consume(parser, TOY_TOKEN_PAREN_RIGHT, "Expected ')' at end of for clause");
//check the postfix clause
if (parser->current.type != TOY_TOKEN_PAREN_RIGHT) {
parsePrecedence(parser, &postClause, PREC_ASSIGNMENT);
consume(parser, TOY_TOKEN_PAREN_RIGHT, "Expected ')' at end of for clause");
}
else {
consume(parser, TOY_TOKEN_PAREN_RIGHT, "Expected ')' after empty increment of for clause");
Toy_emitASTNodePass(&postClause);
}
//read the path
declaration(parser, &thenPath);
+84 -1
View File
@@ -1,10 +1,62 @@
#pragma once
/*!
# toy_parser.h
This header defines the parser structure which, after being initialized with a lexer produces a series of abstract syntax trees to be passed to the compiler. The following is a utility function provided by [repl_tools.h](repl_tools_h.md), demonstrating how to use the parser.
```c
//generate bytecode from a given string
const unsigned char* Toy_compileString(const char* source, size_t* size) {
//declare the relevant instances
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
//initialize each of them
Toy_initLexer(&lexer, source);
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//when the parser returns NULL, it is finished
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//if the parser returns an error node, clean up and exit gracefully
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
//no need to clean the lexer
return NULL;
}
//write the node to the compiler
Toy_writeCompiler(&compiler, node);
Toy_freeASTNode(node);
//grab the next node
node = Toy_scanParser(&parser);
}
//get the bytecode to be returned
const unsigned char* tb = Toy_collateCompiler(&compiler, size);
//cleanup
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
//no need to clean the lexer
//finally
return tb;
}
```
!*/
#include "toy_common.h"
#include "toy_lexer.h"
#include "toy_ast_node.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
//Parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
Toy_Lexer* lexer;
bool error; //I've had an error
@@ -15,6 +67,37 @@ typedef struct {
Toy_Token previous;
} Toy_Parser;
/*!
## Defined Functions
!*/
/*!
### void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer)
This function initializes a `Toy_Parser`, binding the given `Toy_Lexer` to it.
!*/
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
/*!
### void Toy_freeParser(Toy_Parser* parser)
This function frees a `Toy_Parser` once its task is completed.
!*/
TOY_API void Toy_freeParser(Toy_Parser* parser);
/*!
### Toy_ASTNode* Toy_scanParser(Toy_Parser* parser)
This function returns an abstract syntax tree representing part of the program, or an error node. The abstract syntax tree must be passed to `Toy_writeCompiler()` and/or `Toy_freeASTNode()`.
This function should be called repeatedly until it returns `NULL`, indicating the end of the program.
!*/
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
/*!
### void Toy_freeASTNode(Toy_ASTNode* node)
This function cleans up any valid instance of `Toy_ASTNode` pointer passed to it. It is most commonly used to clean up the values returned by `Toy_scanParser`, after they have been passsed to `Toy_writeCompiler`, or when the node is an error node.
Note: this function is *actually* defined in toy_ast_node.h, but documented here, because this is where it matters most.
!*/
+55
View File
@@ -0,0 +1,55 @@
#include "toy_reffunction.h"
#include <string.h>
//memory allocation
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
static Toy_RefFunctionAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn allocator) {
allocate = allocator;
}
//API
Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length) {
//allocate the memory area (including metadata space)
Toy_RefFunction* refFunction = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * length);
if (refFunction == NULL) {
return NULL;
}
//set the data
refFunction->refCount = 1;
refFunction->length = length;
memcpy(refFunction->data, data, refFunction->length);
return refFunction;
}
void Toy_deleteRefFunction(Toy_RefFunction* refFunction) {
//decrement, then check
refFunction->refCount--;
if (refFunction->refCount <= 0) {
allocate(refFunction, sizeof(size_t) + sizeof(int) + sizeof(char) * (refFunction->length + 1), 0);
}
}
int Toy_countRefFunction(Toy_RefFunction* refFunction) {
return refFunction->refCount;
}
size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction) {
return refFunction->length;
}
Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction) {
//Cheaty McCheater Face
refFunction->refCount++;
return refFunction;
}
Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction) {
//create a new function, with a new refCount
return Toy_createRefFunction(refFunction->data, refFunction->length);
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
/*!
# toy_reffunction.h
This header defines the Toy_RefFunction structure, as well as all of the related utilities.
See [Toy_RefString](toy_refstring_h.md) for more information about the reference pattern.
This module reserves the right to instead preform a deep copy when it sees fit (this is for future debugging purposes).
!*/
#include "toy_common.h"
//the RefFunction structure
typedef struct Toy_RefFunction {
size_t length;
int refCount;
unsigned char data[];
} Toy_RefFunction;
/*!
## Defined Interfaces
!*/
/*!
### typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
This interface conforms to Toy's memory API, and generally shouldn't be used without a good reason.
!*/
typedef void* (*Toy_RefFunctionAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
## Defined Functions
!*/
/*!
### void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn)
This function conforms to and is invoked by Toy's memory API, and generally shouldn't be used without a good reason.
!*/
TOY_API void Toy_setRefFunctionAllocatorFn(Toy_RefFunctionAllocatorFn);
/*!
### Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length)
This function returns a new `Toy_RefFunction`, containing a copy of `data`, or `NULL` on error.
This function also sets the returned `refFunction`'s reference counter to 1.
!*/
TOY_API Toy_RefFunction* Toy_createRefFunction(const void* data, size_t length);
/*!
### void Toy_deleteRefFunction(Toy_RefFunction* refFunction)
This function reduces the `refFunction`'s reference counter by 1 and, if it reaches 0, frees the memory.
!*/
TOY_API void Toy_deleteRefFunction(Toy_RefFunction* refFunction);
/*!
### int Toy_countRefFunction(Toy_RefFunction* refFunction)
This function returns the total number of references to `refFunction`, for debugging.
!*/
TOY_API int Toy_countRefFunction(Toy_RefFunction* refFunction);
/*!
### size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction)
This function returns the length of the underlying bytecode of `refFunction`.
!*/
TOY_API size_t Toy_lengthRefFunction(Toy_RefFunction* refFunction);
/*!
### Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction)
This function increases the reference counter of `refFunction` by 1, before returning the given pointer.
This function reserves the right to create a deep copy where needed.
!*/
TOY_API Toy_RefFunction* Toy_copyRefFunction(Toy_RefFunction* refFunction);
/*!
### Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction)
This function behaves identically to `Toy_copyRefFunction`, except that it explicitly forces a deep copy of the internal memory. Using this function should be done carefully, as it incurs a performance penalty that negates the benefit of this module.
!*/
TOY_API Toy_RefFunction* Toy_deepCopyRefFunction(Toy_RefFunction* refFunction);
+18 -24
View File
@@ -1,15 +1,5 @@
#include "toy_refstring.h"
#include <string.h>
#include <assert.h>
//test variable sizes based on platform (safety)
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(Toy_RefString) == 12);
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(char) == 1);
//memory allocation
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
@@ -19,18 +9,22 @@ void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn allocator) {
}
//API
Toy_RefString* Toy_createRefString(char* cstring) {
int length = strlen(cstring);
Toy_RefString* Toy_createRefString(const char* cstring) {
size_t length = strlen(cstring);
return Toy_createRefStringLength(cstring, length);
}
Toy_RefString* Toy_createRefStringLength(char* cstring, int length) {
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length) {
//allocate the memory area (including metadata space)
Toy_RefString* refString = (Toy_RefString*)allocate(NULL, 0, sizeof(int) * 2 + sizeof(char) * length + 1);
Toy_RefString* refString = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * (length + 1));
if (refString == NULL) {
return NULL;
}
//set the data
refString->refcount = 1;
refString->refCount = 1;
refString->length = length;
strncpy(refString->data, cstring, refString->length);
@@ -41,32 +35,32 @@ Toy_RefString* Toy_createRefStringLength(char* cstring, int length) {
void Toy_deleteRefString(Toy_RefString* refString) {
//decrement, then check
refString->refcount--;
if (refString->refcount <= 0) {
allocate(refString, sizeof(int) * 2 + sizeof(char) * refString->length + 1, 0);
refString->refCount--;
if (refString->refCount <= 0) {
allocate(refString, sizeof(size_t) + sizeof(int) + sizeof(char) * (refString->length + 1), 0);
}
}
int Toy_countRefString(Toy_RefString* refString) {
return refString->refcount;
return refString->refCount;
}
int Toy_lengthRefString(Toy_RefString* refString) {
size_t Toy_lengthRefString(Toy_RefString* refString) {
return refString->length;
}
Toy_RefString* Toy_copyRefString(Toy_RefString* refString) {
//Cheaty McCheater Face
refString->refcount++;
refString->refCount++;
return refString;
}
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString) {
//create a new string, with a new refcount
//create a new string, with a new refCount
return Toy_createRefStringLength(refString->data, refString->length);
}
char* Toy_toCString(Toy_RefString* refString) {
const char* Toy_toCString(Toy_RefString* refString) {
return refString->data;
}
@@ -87,7 +81,7 @@ bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs) {
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
//get the rhs length
int length = strlen(cstring);
size_t length = strlen(cstring);
//different length
if (lhs->length != length) {
+117 -19
View File
@@ -1,27 +1,125 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
/*!
# toy_refstring.h
//memory allocation hook
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
This header defines the structure `Toy_RefString`, as well as all of the related utilities.
[refstring](https://github.com/Ratstail91/refstring) is a stand-alone utility written to reduce the amount of memory manipulation used within Toy. It was independantly written and tested, before being incorporated into Toy proper. As such it has it's own memory management API, which by default is tied into Toy's [core memory API](toy_memory_h.md).
Instances of `Toy_RefString` are reference counted - that is, rather than copying an existing string in memory, a pointer to the refstring is returned, and the internal reference counter is increased by 1. When the pointer is no longer needed, `Toy_DeleteRefString` can be called; this will decrement the internal reference counter by 1, and only free it when it reaches 0. This has multiple benefits, when used correctly:
* Reduced memory usage
* Faster program execution
This module reserves the right to instead preform a deep copy when it sees fit (this is for future debugging purposes).
!*/
#include "toy_common.h"
#include <string.h>
//the RefString structure
typedef struct Toy_RefString {
int refcount;
int length;
char data[1];
size_t length;
int refCount;
char data[];
} Toy_RefString;
//API
Toy_RefString* Toy_createRefString(char* cstring);
Toy_RefString* Toy_createRefStringLength(char* cstring, int length);
void Toy_deleteRefString(Toy_RefString* refString);
int Toy_countRefString(Toy_RefString* refString);
int Toy_lengthRefString(Toy_RefString* refString);
Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
char* Toy_toCString(Toy_RefString* refString);
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
/*!
## Defined Interfaces
!*/
/*!
### typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize)
This interface conforms to Toy's memory API, and generally shouldn't be used without a good reason.
!*/
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
/*!
## Defined Functions
!*/
/*!
### void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn)
This function conforms to and is invoked by Toy's memory API, and generally shouldn't be used without a good reason.
!*/
TOY_API void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
/*!
### Toy_RefString* Toy_createRefString(const char* cstring)
This function wraps `Toy_CreateRefStringLength`, by determining the length of the given `cstring` and passing it to the other function.
!*/
TOY_API Toy_RefString* Toy_createRefString(const char* cstring);
/*!
### Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length)
This function returns a new `Toy_RefString`, containing a copy of `cstring`, or `NULL` on error.
This function also sets the returned refstring's reference counter to 1.
!*/
TOY_API Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
/*!
### void Toy_deleteRefString(Toy_RefString* refString)
This function reduces the `refString`'s reference counter by 1 and, if it reaches 0, frees the memory.
!*/
TOY_API void Toy_deleteRefString(Toy_RefString* refString);
/*!
### int Toy_countRefString(Toy_RefString* refString)
This function returns the total number of references to `refString`, for debugging.
!*/
TOY_API int Toy_countRefString(Toy_RefString* refString);
/*!
### size_t Toy_lengthRefString(Toy_RefString* refString)
This function returns the length of the underlying cstring of `refString`.
!*/
TOY_API size_t Toy_lengthRefString(Toy_RefString* refString);
/*!
### Toy_RefString* Toy_copyRefString(Toy_RefString* refString)
This function increases the reference counter of `refString` by 1, before returning the given pointer.
This function reserves the right to create a deep copy where needed.
!*/
TOY_API Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
/*!
### Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString)
This function behaves identically to `Toy_copyRefString`, except that it explicitly forces a deep copy of the internal memory. Using this function should be done carefully, as it incurs a performance penalty that negates the benefit of this module.
!*/
TOY_API Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
/*!
### const char* Toy_toCString(Toy_RefString* refString)
This function exposes the interal cstring of `refString`. Only use this function when dealing with external APIs.
!*/
TOY_API const char* Toy_toCString(Toy_RefString* refString);
/*!
### bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs)
This function returns true when the two refstrings are either the same refstring, or contain the same value. Otherwise it returns false.
!*/
TOY_API bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
/*!
### bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring)
This function returns true when the `refString` contains the same value as the `cstring`. Otherwise it returns false.
!*/
TOY_API bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
//TODO: merge refstring memory
+65 -63
View File
@@ -4,21 +4,19 @@
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Toy_Scope* scope) {
scope->references--;
while (scope != NULL) {
Toy_Scope* next = scope->ancestor;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
scope->references--;
if (scope->references <= 0) {
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
scope = next;
}
if (scope->references > 0) {
return;
}
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
//return false if invalid type
@@ -126,7 +124,7 @@ static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal
}
//find the internal child of original that matches this child of value
Toy_private_entry* ptr = NULL;
Toy_private_dictionary_entry* ptr = NULL;
for (int j = 0; j < TOY_AS_DICTIONARY(original)->capacity; j++) {
if (Toy_literalsAreEqual(TOY_AS_DICTIONARY(original)->entries[j].key, TOY_AS_DICTIONARY(value)->entries[i].key)) {
@@ -159,6 +157,10 @@ static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal
return false;
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_OPAQUE && !TOY_IS_OPAQUE(value)) {
return false;
}
return true;
}
@@ -185,7 +187,7 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
Toy_Scope* ret = scope->ancestor;
//BUGFIX: when freeing a scope, free the function's scopes manually
//BUGFIX: when freeing a scope, free the functions' scopes manually - I *think* this is related to the closure hack-in
for (int i = 0; i < scope->variables.capacity; i++) {
//handle keys, just in case
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
@@ -205,6 +207,10 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
}
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
if (original == NULL) {
return NULL;
}
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
scope->ancestor = original->ancestor;
Toy_initLiteralDictionary(&scope->variables);
@@ -250,75 +256,71 @@ bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal typ
return true;
}
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
if (scope == NULL) {
return false;
bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
return true;
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
return false;
}
//return false if undefined, or can't be assigned
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
while (scope != NULL) {
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
scope = scope->ancestor;
continue;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value); //key & value are copied here
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
return true;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value);
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return true;
return false;
}
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
//optimized to reduce call stack
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
return false;
}
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
//dead end
if (scope == NULL) {
return TOY_TO_NULL_LITERAL;
while (scope != NULL) {
if (Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getLiteralDictionary(&scope->types, key);
}
scope = scope->ancestor;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getScopeType(scope->ancestor, key);
}
return Toy_getLiteralDictionary(&scope->types, key);
return TOY_TO_NULL_LITERAL;
}
+75 -10
View File
@@ -1,5 +1,16 @@
#pragma once
/*!
# toy_scope.h
This header defines the scope structure, which stores all of the variables used within a given block of code.
Scopes are arranged into a linked list of ancestors, each of which is reference counted. When a scope is popped off the end of the chain, every ancestor scope has it's reference counter reduced by 1 and, if any reach 0, they are freed.
This is also where Toy's type system lives.
!*/
#include "toy_literal.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
@@ -10,16 +21,70 @@ typedef struct Toy_Scope {
int references; //how many scopes point here
} Toy_Scope;
Toy_Scope* Toy_pushScope(Toy_Scope* scope);
Toy_Scope* Toy_popScope(Toy_Scope* scope);
Toy_Scope* Toy_copyScope(Toy_Scope* original);
/*!
## Defined Functions
!*/
//returns false if error
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
/*!
### Toy_Scope* Toy_pushScope(Toy_Scope* scope)
//return false if undefined
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
This function creates a new `Toy_scope` with `scope` as it's ancestor, and returns it.
!*/
TOY_API Toy_Scope* Toy_pushScope(Toy_Scope* scope);
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
/*!
### Toy_Scope* Toy_popScope(Toy_Scope* scope)
This function frees the given `scope`, and returns it's ancestor.
!*/
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
/*!
### Toy_Scope* Toy_copyScope(Toy_Scope* original)
This function copies an existing scope, and returns the copy.
This copies the internal dictionaries, so it can be memory intensive.
!*/
TOY_API Toy_Scope* Toy_copyScope(Toy_Scope* original);
/*!
### bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type)
This function declares a new variable `key` within `scope`, giving it the type of `type`.
This function returns true on success, otherwise it returns failure (such as if the given key already exists).
!*/
TOY_API bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
/*!
### bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key)
This function checks to see if a given variable with the name `key` has been previously declared.
!*/
TOY_API bool Toy_isDeclaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
/*!
### bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck)
This function sets an existing variable named `key` to the value of `value`. This function fails if `constCheck` is true and the given key's type has the constaant flag set. It also fails if the given key doesn't exist.
This function returns true on success, otherwise it returns false.
!*/
TOY_API bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
/*!
### bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value)
This function sets the literal pointed to by `value` to equal the variable named `key`.
This function returns true on success, otherwise it returns false.
!*/
TOY_API bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
/*!
### Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key)
This function returns a new `Toy_Literal` representing the type of the variable named `key`.
!*/
TOY_API Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
+2 -2
View File
@@ -74,8 +74,8 @@ typedef enum Toy_TokenType {
TOY_TOKEN_GREATER,
TOY_TOKEN_LESS_EQUAL,
TOY_TOKEN_GREATER_EQUAL,
TOY_TOKEN_AND,
TOY_TOKEN_OR,
TOY_TOKEN_AND_AND,
TOY_TOKEN_OR_OR,
//other operators
TOY_TOKEN_QUESTION,
+3 -3
View File
@@ -2,9 +2,9 @@ CC=gcc
IDIR +=. ../source ../repl
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS +=
LIBS +=-lm
ODIR = obj
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c) ../repl/repl_tools.c
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c) ../repl/repl_tools.c ../repl/drive_system.c
TESTS = $(wildcard test_*.c)
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
@@ -15,7 +15,7 @@ all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
valgrind --leak-check=full --track-origins=yes $@
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all $@
else
$@
endif
+7
View File
@@ -38,5 +38,12 @@ s += "bar";
assert s == "foobar", "string addition failed (wasn't sticky enough)";
//check order of operations
assert 30 / 3 * 2 == 20, "Order of operations failed (raw numbers)";
var x = 30;
var y = 3;
var z = 2;
assert x / y * z == 20, "Order of operations failed (variables)";
print "All good";
@@ -0,0 +1,7 @@
var s = "42";
var t = "69";
assert int (s + t) - 1 == 4268, "casting parentheses failed";
print "All good";
+16
View File
@@ -10,4 +10,20 @@
}
//test function coercion
{
fn f(arg: float) {
assert typeof arg == float, "argument coercion failed";
}
f(42);
fn g(): float {
return 42;
}
assert typeof g() == float, "return coercion failed";
}
print "All good";
+3
View File
@@ -23,5 +23,8 @@ assert !false, "!false";
var c = false;
assert !c, "!c";
//test multiple comparisons
assert 1 == 2 == false, "Left-accociative equality failed";
print "All good";
+17 -17
View File
@@ -2,11 +2,11 @@
var a = [1, 2, 3];
var b = [4, 5, 6];
assert _length(a) == _length(b), "a and b lengths are wrong";
assert length(a) == length(b), "a and b lengths are wrong";
var acc = 0;
for (var i = 0; i < _length(a); i++) {
acc += _get(a, i) * _get(b, i);
for (var i = 0; i < length(a); i++) {
acc += get(a, i) * get(b, i);
}
assert acc == 32, "dot product failed";
@@ -15,38 +15,38 @@ assert acc == 32, "dot product failed";
//assume the args are matrices
fn matrix(first, second) {
//get the matrix size
var l1 = _length(first); //rows
var l2 = _length(_get(first, 0)); //cols
var l1 = length(first); //rows
var l2 = length(get(first, 0)); //cols
var l3 = _length(second); //rows
var l4 = _length(_get(second, 0)); //cols
var l3 = length(second); //rows
var l4 = length(get(second, 0)); //cols
//pre-allocate the matrix
var row = [];
for (var j = 0; j < l4; j++) {
_push(row, 0);
push(row, 0);
}
var result = [];
for (var i = 0; i < l1; i++) {
_push(result, row);
push(result, row);
}
//assign the values
for (var i = 0; i < _length(first); i++) {
for (var i = 0; i < length(first); i++) {
//select each element of "first"
var firstElement = _get(first, i);
var firstElement = get(first, i);
//for each element of second
for (var i2 = 0; i2 < _length(second); i2++) {
for (var j2 = 0; j2 < _length(_get(second, 0)); j2++) {
for (var i2 = 0; i2 < length(second); i2++) {
for (var j2 = 0; j2 < length(get(second, 0)); j2++) {
var val = _get(_get(first, i), i2) * _get(_get(second, i2), j2);
var val = get(get(first, i), i2) * get(get(second, i2), j2);
//TODO: needs better notation than this tmpRow variable
var tmpRow = _get(result, i);
_set(tmpRow, j2, val);
_set(result, i, tmpRow);
var tmpRow = get(result, i);
set(tmpRow, j2, val);
set(result, i, tmpRow);
//result[ i ][ j2 ] += first[i][i2] * second[i2][j2]
}
+1 -1
View File
@@ -9,7 +9,7 @@ It appears to be a compiler issue, see issue #38 for more info.
*/
fn _getValue(self) {
fn getValue(self) {
return self;
}
+3 -3
View File
@@ -1,10 +1,10 @@
//test function chaining with the dot operator
fn _identity(self) {
fn identity(self) {
return self;
}
fn _check(self) {
fn check(self) {
assert self == 42, "dot chaining failed";
return self;
}
@@ -20,7 +20,7 @@ val
//test the value is actually altered
fn _increment(self) {
fn increment(self) {
return self + 1;
}
+23
View File
@@ -0,0 +1,23 @@
var days = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday"
];
var rng = 10; //for chosen at random
var index = rng % days.length();
assert index == 3, "dot modulo bugfix failed";
rng %= days.length();
assert rng == 3, "dot modulo assign bugfix failed";
print "All good";
+1 -1
View File
@@ -1,5 +1,5 @@
fn _add(self, inc) {
fn add(self, inc) {
return self + inc;
}
@@ -0,0 +1,30 @@
{
fn a() {
fn b() {
return 42;
}
return b;
}
assert a()() == 42, "function within function failed";
}
{
fn a() {
fn b() {
fn c() {
return 42;
}
return c;
}
return b;
}
assert a()()() == 42, "function within function within function failed";
}
print "All good";
+1 -1
View File
@@ -63,7 +63,7 @@ extra("one", "two", "three", "four", "five", "six", "seven");
//test underscore functions
fn _example(self, a, b, c) {
fn example(self, a, b, c) {
assert a == "a", "underscore failed (a)";
assert b == "b", "underscore failed (b)";
assert c == "c", "underscore failed (c)";
+11
View File
@@ -0,0 +1,11 @@
//test for casting + grouping, see #67
{
assert string(10 % 4) == "2", "basic group casting failed";
assert string 4 == "4", "normal casting failed";
assert typeof string(10 % 4) == string, "group casting type failed";
}
print "All good";
+15
View File
@@ -0,0 +1,15 @@
var a = 0;
if (a++ >= 1) {
assert false, "increment postfix bugfix failed (first check)";
}
if (a++ >= 1) {
}
assert a == 2, "increment postfix bugfix failed (second check)";
print "All good";
+10
View File
@@ -92,4 +92,14 @@
}
//test deep-combine example
{
fn combine(a, b, c, d) {
return [[a, b], [c, d]];
}
assert combine(1, 2, 3, 4) == [[1, 2], [3, 4]], "deep-combine example failed";
}
print "All good";
@@ -0,0 +1,15 @@
/*
This ensures that when indexing on both sides of an assignment,
it works correctly.
*/
var a = [1, 2, 3];
var b = [4, 5, 6];
a[1] = b[1];
assert a == [1, 5, 3], "index assignment both failed";
print "All good";
@@ -0,0 +1,10 @@
var result; //result must exist to ensure assingment, rather than declaration is invoked by the comparison below
var lhs = [0];
var rhs = [0];
result = lhs[0] < rhs[0]; //make sure this doesn't invoke TOY_OP_INDEX_ASSIGN_INTERMEDIATE
print "All good";
@@ -0,0 +1,19 @@
/*
Compiler note:
This is also to test a specific element in the compiler.
It ensures that when doing indexing and assignment in one statement,
the index is NOT on the right. If it is, then it is treated like a normal
assignment.
*/
//polyfill the _insert function
var a = [1, 2, 3];
var b = a[1];
assert b == 2, "index assignment left failed";
print "All good";
@@ -0,0 +1,20 @@
fn max(lhs, rhs) {
if (lhs > rhs) {
return lhs;
}
else {
return rhs;
}
}
var array = [42];
var result = null;
//problematic line
result = max(0, array[0]);
assert result == 42, "Indexing in argument list failed";
print "All good";
-189
View File
@@ -1,189 +0,0 @@
import compound;
//test concat
{
//test array concat
{
var a = [1, 2, 3];
var b = [4, 5, 6];
var c = a.concat(b).concat(b);
assert c == [1, 2, 3, 4, 5, 6, 4, 5, 6], "array.concat() failed";
}
//test dictionary concat
{
var a = ["one" : 1, "two": 2, "three": 3];
var b = ["four" : 4, "five": 5, "six": 6];
var c = a.concat(b);
assert c.length() == 6, "dictionary.concat().length() failed";
assert c == ["one" : 1, "two": 2, "three": 3, "four" : 4, "five": 5, "six": 6], "dictionary.concat() comparison failed";
}
//test dictionary concat with clashing keys
{
var a = ["one" : 1, "two": 2, "three": 3, "random": 1];
var b = ["four" : 4, "five": 5, "six": 6, "random": 2];
var c = a.concat(b);
assert c["random"] == 1, "dictionary.concat() clashing keys failed";
}
//test string concat
{
var a = "foo";
var b = "bar";
var c = a.concat(b);
assert c == "foobar", "string.concat() failed";
}
}
//test forEach
{
var counter = 0;
fn p(k, v) {
counter++;
print string k + ": " + string v;
}
var a = ["a", "b"];
var d = ["foo": 1, "bar": 2, "bazz": 3, "fizz": 4];
a.forEach(p);
assert counter == 2, "forEach ran an unusual number of times";
counter = 0;
d.forEach(p);
assert counter == 4, "forEach ran an unusual number of times";
}
//test getKeys
{
var d = ["foo": 1, "bar": 2];
var a = d.getKeys();
assert a.length() == 2, "_getKeys() length failed";
//NOTE: dependant on hash algorithm
assert a == ["bar", "foo"], "_getKeys() result failed";
}
//test getValues
{
var d = ["foo": 1, "bar": 2];
var a = d.getValues();
assert a.length() == 2, "_getValues() length failed";
//NOTE: dependant on hash algorithm
assert a == [2, 1], "_getValues() result failed";
}
//test map
{
//test map with toy functions
{
fn increment(k, v) {
return v + 1;
}
var a = [1, 2, 3];
var d = ["four": 4, "five": 5, "six": 6];
assert a.map(increment).map(increment).map(increment) == [4,5,6], "array.map() failed";
assert d.map(increment).map(increment).map(increment) == [8,9,7], "dictionary.map() failed";
}
//test map with native functions
{
//TODO: write some native functions for use with map
}
}
//test reduce
{
var a = [1, 2, 3, 4];
var d = ["one": 1, "two": 2, "three": 3, "four": 4];
fn f(acc, k, v) {
return acc + v;
}
assert a.reduce(0, f) == 10, "array.reduce() failed";
assert d.reduce(0, f) == 10, "dictionary.reduce() failed";
}
//test toLower
{
assert "Hello World".toLower() == "hello world", "_toLower() failed";
}
//test toString
{
var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
var s = a.toString();
assert s == "[[1,2,3],[4,5,6],[7,8,9]]", "array._toString() failed";
}
//test toUpper
{
assert "Hello World".toUpper() == "HELLO WORLD", "_toUpper() failed";
}
//test trim defaults
{
{
//test a bunch
fn test(s, pass) {
var result = s.trim();
assert result == pass, "_trim(" + result + ") failed";
}
test("hello world", "hello world");
test(" hello world", "hello world");
test("hello world ", "hello world");
test(" hello world ", "hello world");
test(" hello world", "hello world");
test("hello world ", "hello world");
test(" hello world ", "hello world");
test(" hello world", "hello world");
test("hello world ", "hello world");
test(" hello world ", "hello world");
//one for goot luck
assert " hello world ".trim() == "hello world", "hello world.trim() failed";
}
//test trim custom values
{
var chars = "heliod";
assert "hello world".trim(chars) == " wor", "custom _trim() failed";
}
//test trimBegin() & trimEnd()
assert " foo ".trimBegin() == "foo ", "string.trimBegin() failed";
assert " foo ".trimEnd() == " foo", "string.trimBegin() failed";
}
print "All good";
+1
View File
@@ -0,0 +1 @@
Hello, World!
+4
View File
@@ -0,0 +1,4 @@
1
8
12.5
test
+3
View File
@@ -0,0 +1,3 @@
08
12.500000
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+164
View File
@@ -0,0 +1,164 @@
import fileio;
var PATH: string const = "scripts:/lib/file/fileio.txt";
// reset file to orginal state
fn reset() {
var writer = open(PATH, "w");
writer.write("Hello, World!\n");
writer.close();
}
// test global constants
{
assert MAX_FILENAME_SIZE > 0, "MAX_FILENAME_SIZE failed";
assert MAX_FILES_OPEN > 0, "MAX_FILES_OPEN failed";
assert END_OF_FILE == -1, "END_OF_FILE failed";
}
// test read
{
var reader = open("scripts:/lib/file/inputs.txt", "r");
assert reader.read(bool) == true, "read true bool failed";
assert reader.read(int) == 8, "read int failed";
assert reader.read(float) == 12.5, "read float failed";
assert reader.read(string) == "\ntest", "read string failed";
// invaild types
assert reader.read(type) == null, "read type failed";
assert reader.read(any) == null, "read any failed";
reader.close();
}
// test write
{
var writer = open("scripts:/lib/file/outputs.txt", "w");
assert writer.write(false) == true, "write bool failed";
assert writer.write(8) == true, "write int failed";
assert writer.write("\n") == true, "write string failed";
assert writer.write(12.5) == true, "write float failed";
assert writer.write("\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n") == true, "write long string failed";
// invaild types
assert writer.write([0, 1, 2]) == false, "write array failed";
assert writer.write(["hi": "world"]) == false, "write dict failed";
writer.close();
}
// test open and close
{
var reader = open(PATH);
assert reader != null, "open failed in reading mode";
assert reader.close() == true, "close failed";
var file = open("scripts:/doesNotExist", "r");
assert file == null, "open failed on nonexisting file";
}
// test append
{
var appender = open(PATH, "a");
assert appender != null, "open failed on appending file";
assert appender.write("appended text") == true, "append failed";
appender.close();
reset();
}
// test read extended
{
var reader = open(PATH, "r+");
assert reader != null, "open failed on read extended mode";
assert reader.write("writen text") == true, "write in read extended failed";
var result = reader.read(string);
// print result;
// assert (result == "d!\n" || result == "d!\\r\n"), "read in read extended failed";
reader.close();
reset();
}
// test write extended
{
var writer = open(PATH, "w+");
assert writer != null, "open failed on write extended mode";
assert writer.write("writen text") == true, "write in write extended failed";
writer.seek("bgn", 0);
assert writer.read(string) == "writen text", "read in write extended failed";
writer.close();
reset();
}
// test rename
{
var reader = open(PATH, "r");
assert reader.rename("scripts:/lib/file/newName.txt") == true, "rename failed";
reader.rename(PATH);
reader.close();
}
// test seek
{
var reader = open(PATH, "r");
assert reader.seek("bgn", 6) == true, "seek from bgn failed";
var contents = reader.read(string);
assert contents == " World!\n", "seek failed to move file position (1st)";
assert reader.seek("end", -2) == true, "seek from end failed";
contents = reader.read(string);
print ">>>(" + contents + ")" + string( contents.length() );
assert contents == "\n", "seek failed to move file position (2nd)";
assert reader.seek("cur", -2) == true, "seek from cur failed";
contents = reader.read(string);
assert contents == "\n", "seek failed to move file position (3rd)";
assert reader.seek("CUR", 0) == false, "seek origin failed (1st)";
assert reader.seek("End", 0) == false, "seek origin failed (2nd)";
assert reader.seek("beG", 0) == false, "seek origin failed (3rd)";
assert reader.seek("xxx", 0) == false, "seek origin failed (4th)";
reader.close();
}
// test accessors
{
var reader = open(PATH, "r");
assert reader.error() == false, "error failed";
assert reader.completed() == false, "completed failed";
assert reader.position() == 0, "position failed";
assert reader.size() == 14, "size failed";
assert reader.mode() == "r", "mode failed";
reader.read(string);
assert reader.error() == false, "error failed";
assert reader.completed() == true, "completed after read failed";
assert reader.position() == 14, "position failed";
assert reader.size() == 14, "size failed";
assert reader.mode() == "r", "mode failed";
reader.close();
}
input.close();
output.close();
print "All good";
+187
View File
@@ -0,0 +1,187 @@
import math;
// test pow
{
assert pow(5, 3) == 125, "pow(5, 3) failed";
assert pow(-5, 3) == -125, "pow(-5, 3) failed";
assert pow(-5.5, 3) == -166.375, "pow(-5.5, 3) failed";
assert pow(0, 1) == 0.0, "pow(0, 1) failed";
assert pow(-0.0, 1) == -0.0, "pow(0, 1) failed";
}
// test sqrt
{
assert sqrt(25) == 5, "sqrt(25) failed";
assert sqrt(256.0) == 16, "sqrt(256.0) failed";
assert checkIsNaN(sqrt(-256.0)), "sqrt(-256.0) failed";
assert sqrt(1) == 1, "sqrt(1) failed";
assert sqrt(0) == 0, "sqrt(0) failed";
}
// test cbrt
{
assert cbrt(64) == 4, "cbrt(64) failed";
assert cbrt(4096.0) == 16, "cbrt(4096.0) failed";
assert cbrt(-64) == -4, "cbrt(-64) failed";
assert cbrt(1) == 1, "cbrt(1) failed";
assert cbrt(0) == 0, "cbrt(0) failed";
}
// test hypot
{
assert hypot(3, 4) == 5, "hypot(3, 4) failed";
}
// test toRad
{
assert toRadians(0) == 0, "toRadians(0) failed";
assert toRadians(180) == PI, "toRadians(180) failed";
assert toRadians(360) == 2 * PI, "toRadians(360) failed";
}
// test toDeg
{
assert toDegrees(0) == 0, "toDegrees(0) failed";
assert toDegrees(PI) == 180, "toDegrees(PI) failed";
assert toDegrees(2 * PI) == 360, "toDegrees(2*PI) failed";
}
// test sin
{
assert epsilionCompare(sin(PI), 0), "sin(PI) failed";
assert epsilionCompare(sin(PI / 2), 1), "sin(PI/2) failed";
assert epsilionCompare(sin(0), 0), "sin(0) failed";
}
// test cos
{
assert epsilionCompare(cos(PI), -1), "cos(PI) failed";
assert epsilionCompare(cos(PI / 2), 0), "cos(PI/2) failed";
assert epsilionCompare(cos(0), 1), "cos(0) failed";
}
// test tan
{
assert epsilionCompare(tan(PI), 0), "tan(PI) failed";
assert epsilionCompare(tan(PI / 4), 1), "tan(PI/4) failed";
assert epsilionCompare(tan(0), 0), "tan(0) failed";
}
// test asin
{
assert epsilionCompare(asin(1), 1.570796), "asin(1) failed";
assert epsilionCompare(asin(-0.5), -0.523599), "asin(-0.5) failed";
assert epsilionCompare(asin(0), 0), "asin(0) failed";
}
// test acos
{
assert epsilionCompare(acos(1), 0), "acos(1) failed";
assert epsilionCompare(acos(0.5), 1.047198), "acos(0.5) failed";
assert epsilionCompare(acos(0), 1.570796), "acos(0) failed";
}
// test atan
{
assert epsilionCompare(atan(1), 0.785398), "acos(1) failed";
assert epsilionCompare(atan(INFINITY), 1.570796), "atan(INFINITY) failed";
assert epsilionCompare(atan(0), 0), "atan(0) failed";
}
// test atan2
{
assert epsilionCompare(atans(0, 0), 0), "atan2(0, 0) failed";
assert epsilionCompare(atans(7, 0), 1.570796), "atans(7, 0) failed";
}
// test sinh
{
assert epsilionCompare(sinh(1), 1.175201), "sinh(1) failed";
assert epsilionCompare(sinh(-1), -1.175201), "sinh(-1) failed";
assert epsilionCompare(sinh(0), 0), "sinh(0) failed";
}
// test cosh
{
assert epsilionCompare(cosh(1), 1.543081), "cosh(1) failed";
assert epsilionCompare(cosh(-1), 1.543081), "cosh(-1) failed";
assert epsilionCompare(cosh(0), 1), "cosh(0) failed";
}
// test tanh
{
assert epsilionCompare(tanh(1), 0.761594), "tanh(1) failed";
assert epsilionCompare(tanh(-1), -0.761594), "tanh(-1) failed";
assert epsilionCompare(tanh(0), 0), "tanh(0) failed";
}
// test asinh
{
assert epsilionCompare(asinh(1), 0.881374), "asinh(1) failed";
assert epsilionCompare(asinh(-1), -0.881374), "asinh(-1) failed";
assert epsilionCompare(asinh(0), 0), "asinh(0) failed";
}
// test acosh
{
assert epsilionCompare(acosh(1), 0), "acosh(1) failed";
assert checkIsNaN(acosh(-1)) == true, "acosh(-1) failed";
assert checkIsNaN(acosh(0)) == true, "acosh(0) failed";
}
// test atanh
{
assert checkIsInfinite(atanh(1)) == true, "atanh(1) failed";
assert checkIsInfinite(atanh(-1)) == true, "atanh(-1) failed";
assert epsilionCompare(atanh(0), 0), "atanh(0) failed";
}
// test checkIsNaN
{
assert checkIsNaN(NAN) == true, "checkIsNaN(NAN) failed";
assert checkIsNaN(INFINITY) == false, "checkIsNaN(INFINITY) failed";
assert checkIsNaN(0.0) == false, "checkIsNaN(0.0) failed";
assert checkIsNaN(INFINITY - INFINITY) == true, "checkIsNaN(INFINITY - INFINITY) failed";
}
// test checkIsFinite
{
assert checkIsFinite(NAN) == false, "checkIsFinite(NAN) failed";
assert checkIsFinite(INFINITY) == false, "checkIsFinite(INFINITY) failed";
assert checkIsFinite(0.0) == true, "checkIsFinite(0.0) failed";
assert checkIsFinite(1) == true, "checkIsFinite(1) failed";
}
// test checkIsInfinite
{
assert checkIsInfinite(NAN) == false, "checkIsInfinite(NAN) failed";
assert checkIsInfinite(INFINITY) == true, "checkIsInfinite(INFINITY) failed";
assert checkIsInfinite(0.0) == false, "checkIsInfinite(0.0) failed";
assert checkIsInfinite(1) == false, "checkIsInfinite(1) failed";
}
// test epsilionCompare
{
assert epsilionCompare(1, 1) == true, "epsilionCompare(1, 1) failed";
assert epsilionCompare(1, 1.000001) == true, "epsilionCompare(1, 1.000001) failed";
assert epsilionCompare(1, 1.001) == false, "epsilionCompare(1, 1.001) failed";
assert epsilionCompare(0, 0) == true, "epsilionCompare(0, 0) failed";
}
print "All good";
-40
View File
@@ -1,40 +0,0 @@
//test this logic for memory leaks
{
import compound;
import timer;
fn start(k, v) {
return startTimer();
}
fn check(k, v) {
var l = v.stopTimer();
print l.timerToString();
l.destroyTimer();
return v;
}
fn destroy(k, v) {
v.destroyTimer();
}
var arr = [1];
arr
.map(start)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(destroy)
;
}
print "All good";
+17
View File
@@ -0,0 +1,17 @@
import standard;
import random;
var generator: opaque = createRandomGenerator(clock().hash()); //create a new generator object, from a non-determinant source
var a: int = generator.generateRandomNumber();
var b: int = generator.generateRandomNumber();
var c: int = generator.generateRandomNumber();
generator.freeRandomGenerator();
assert a != b, "random a != random b failed";
assert a != c, "random a != random c failed";
assert b != c, "random b != random c failed";
print "All good";
Binary file not shown.

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