Compare commits

..

273 Commits

Author SHA1 Message Date
Kayne Ruse 3498baad9b Resolved #45, Exports region removed 2023-01-13 16:12:44 +00:00
Kayne Ruse 0649a141dd Moved time header includes, comment tweaks 2023-01-13 15:39:22 +00:00
Kayne Ruse 65e5905011 Updated license dates 2023-01-11 20:48:47 +00:00
Kayne Ruse bf64275aa9 Floats will always print with a trailing number 2023-01-08 13:26:04 +00:00
Kayne Ruse 08b400debf Added dictionary key mustfail test 2023-01-08 12:58:02 +00:00
Kayne Ruse 9ad2a6cf2e Tweaked build message 2023-01-08 23:46:04 +11:00
Kayne Ruse 8009f410a4 Added mustfail test, fixed compound type decl bug 2023-01-08 12:43:25 +00:00
Kayne Ruse 584fb115b6 Fixed the awful rule110 implementation 2022-11-28 16:48:45 +00:00
Kayne Ruse 70698a4a1a Simplified an optimisation 2022-11-27 02:25:33 +00:00
Kayne Ruse eb26d23363 Added FUNDING.yml, because why is this so popular? 2022-11-27 02:14:03 +00:00
Kayne Ruse c1625c53f4 Patched a bug in deepCopyRefString() 2022-11-26 21:33:46 +00:00
Kayne Ruse fb55f42d0e Tweaked where strlen() is called 2022-11-26 15:53:07 +00:00
Kayne Ruse e4ef35092f Merge pull request #43 from Ratstail91/dev
refstring implementation to handle strings and identifiers
2022-11-26 21:43:35 +11:00
Kayne Ruse cfafba589b All tests passing, repl builds successfully 2022-11-26 09:53:59 +00:00
Kayne Ruse f6367fa89d Updated interpreter 2022-11-26 09:43:19 +00:00
Kayne Ruse d199209772 Updated compiler 2022-11-26 06:58:38 +00:00
Kayne Ruse 4cf5c6a5bf Updated parser 2022-11-26 01:59:37 +00:00
Kayne Ruse 30c3a890ee Adjusted how AST Nodes are generated 2022-11-25 12:29:35 +00:00
Kayne Ruse 130ac980fe Enabled lexer tests 2022-11-23 13:02:43 +00:00
Kayne Ruse 923cf70c06 Minimal build of the refstrings in the literal structure 2022-11-23 12:52:49 +00:00
Kayne Ruse c7465e1204 Added API macro 2022-11-19 08:33:08 +00:00
Kayne Ruse 4518b59417 Added custom allocator support 2022-11-19 08:18:45 +00:00
Kayne Ruse 0b57f7efe7 Added release build targets, because bored 2022-11-16 22:05:47 +00:00
Kayne Ruse 7bf18a744c Tweaked bounds check 2022-11-12 11:39:32 +00:00
Kayne Ruse fa20763c07 Corrected negative timers 2022-11-12 11:30:24 +00:00
Kayne Ruse 774f3d9e83 Corrected error messages in the timer lib 2022-11-12 10:07:54 +00:00
Kayne Ruse 2d18ff4ba3 Fixed some printf formats in the tests 2022-11-12 03:26:14 +00:00
Kayne Ruse 30b068fcdb Fixed timer issues, tests should pass
Reduced test duration by a factor of 11,000. Don't ask.

Also, something funny is going on with the time headers, so I
stuck them into source/toy_common.h, I'll figure it out later.
2022-11-12 04:45:35 +11:00
Kayne Ruse 3d8ce4e7d8 Trying to fix time and platforms 2022-11-11 17:31:45 +00:00
Kayne Ruse e0ab4106fa Implemented the timer library 2022-11-11 17:18:07 +00:00
Kayne Ruse 2c143a8be5 Moved tests from scripts/ to test/scripts/ 2022-11-11 14:51:47 +00:00
Kayne Ruse 0aa6e4063b Spotted a scope issue in the test 2022-11-08 20:22:20 +00:00
Kayne Ruse ec39f099ca Wrote failing TDD test for timer library, not enabled 2022-11-08 19:40:21 +00:00
Kayne Ruse 4dcc05e796 Corrected fnv1 hash algorithm 2022-11-08 19:36:54 +00:00
Kayne Ruse 2af95ec82e Tweak 2022-11-08 02:55:02 +00:00
Kayne Ruse bbdb521333 Merge remote-tracking branch 'refs/remotes/origin/main' 2022-11-08 02:53:07 +00:00
Kayne Ruse 56987bc96a Tweaked colors for types 2022-11-08 02:52:18 +00:00
Kayne Ruse 8498864dde Resolved a name clash with the engine 2022-11-07 16:38:39 +00:00
Kayne Ruse 14710dec90 Tweaked README.md 2022-11-07 10:12:15 +00:00
Kayne Ruse d14177dbca Made it easier to install syntax highlighting 2022-11-07 10:02:37 +00:00
Kayne Ruse 42580bbe2a Update toy.tmLanguage.json
Forgot opaque type
2022-11-07 20:47:37 +11:00
Kayne Ruse 0c8e036de8 Added vscode syntax highlighting under /tools 2022-11-07 09:44:26 +00:00
Kayne Ruse a55338d8e3 Tweaked README.md 2022-11-06 04:15:33 +00:00
Kayne Ruse 5d240f85a6 BUGFIX: chained calls not being dottified 2022-11-04 11:13:40 +01:00
Kayne Ruse cceefa6375 Resolved #38 2022-11-03 16:25:29 +01:00
Kayne Ruse 632ed7c089 The tests did not like that 2022-11-03 22:59:25 +11:00
Kayne Ruse c1528f5501 Parallelized compilation 2022-11-03 12:54:41 +01:00
Kayne Ruse 6c5d952c44 Tests are ok for now 2022-10-20 00:00:13 +01:00
Kayne Ruse 208ad9d615 Experimenting 2022-10-20 09:45:28 +11:00
Kayne Ruse 876aad853c Creating the CI file 2022-10-20 09:42:52 +11:00
Kayne Ruse 1baa65cc95 Removed annoying assertion test messages from test output 2022-10-19 23:34:15 +01:00
Kayne Ruse e01e096188 Removed spec.md, use https://toylang.com 2022-10-16 11:10:57 +01:00
Kayne Ruse 78ce3bdb99 Added to a test 2022-10-16 10:35:44 +01:00
Kayne Ruse bd348abf32 Added dot chaining for functions
Well, it should work without issues...
2022-10-14 23:58:15 +01:00
Kayne Ruse 168369d897 Moved the license into it's own file 2022-10-07 23:42:31 +11:00
Kayne Ruse 8b04939430 Fixed a typo 2022-10-06 14:23:50 +11:00
Kayne Ruse 8a8074ee6e Update README.md 2022-10-04 17:03:47 +11:00
Kayne Ruse 5686677383 Added tags to opaque data type 2022-10-03 22:13:56 +01:00
Kayne Ruse bd4ab2aa04 Added the opaque keyword 2022-10-03 21:32:09 +01:00
Kayne Ruse 8ce7dd0d95 typo = memory leak 2022-10-04 07:04:44 +11:00
Kayne Ruse ca24c4f211 Added the opaque data type 2022-10-03 21:02:13 +01:00
Kayne Ruse 016ab9c5fe Fixed the tests 2022-10-03 20:24:19 +01:00
Kayne Ruse 8072beb007 Moved repl's tools into their own file 2022-10-03 20:18:06 +01:00
Kayne Ruse 99bcffe77c Typo in a comment 2022-10-02 16:22:30 +11:00
Kayne Ruse 5c7e8bba0d Comment Tweak 2022-10-02 16:07:21 +11:00
Kayne Ruse 3460967e3b Added API macro to some functions 2022-10-02 00:44:46 +01:00
Kayne Ruse 467cd8d978 Resolved #26, shared libs work on linux 2022-10-02 04:09:27 +11:00
Kayne Ruse 50cc5ca270 Plugged memory leaks in the test 2022-10-01 23:00:16 +10:00
Kayne Ruse 256538e1f9 Added a utility to call toy functions from C 2022-10-01 13:51:40 +01:00
Kayne Ruse 976a9073f8 Tweaked the spec 2022-09-24 08:16:27 +01:00
Kayne Ruse aa44d5fd43 Added aliasing of standard as a feature of standard 2022-09-24 07:20:26 +01:00
Kayne Ruse f745470abe Resolved #31, added exit and quit commands to the repl 2022-09-24 06:41:09 +01:00
Kayne Ruse 04771b1f81 Tweaked README.md 2022-09-24 15:10:23 +10:00
Kayne Ruse 9969cbe11a Merge pull request #30 from GruelingPine185/0.6.0
Mac Support
2022-09-24 14:52:43 +10:00
GruelingPine185 48dbbfaa24 build repl 2022-09-23 18:22:42 -04:00
GruelingPine185 d77711da7b build library 2022-09-23 18:22:20 -04:00
GruelingPine185 57e7acf97a updated README.md Building section 2022-09-23 18:18:46 -04:00
GruelingPine185 32aad5eecc cleanup files 2022-09-23 18:17:09 -04:00
GruelingPine185 ecbfcc28b5 resolved line ending warnings 2022-09-23 18:16:53 -04:00
GruelingPine185 2b0b678e25 added Bear ignores 2022-09-23 18:11:04 -04:00
Kayne Ruse 6a086395be Renamed Node to ASTNode 2022-09-18 19:14:37 +01:00
Kayne Ruse 2458996ee7 Tweak 2022-09-18 14:39:13 +01:00
Kayne Ruse 7a15e645a7 screwing around 2022-09-18 09:31:25 +01:00
Kayne Ruse 73626da5be Fixed platform stuff 2022-09-18 09:29:03 +01:00
Kayne Ruse c680427b1e Tweaked a build var name 2022-09-18 07:26:23 +01:00
Kayne Ruse 64cc514171 Expanded the tests a little 2022-09-18 07:03:18 +01:00
Kayne Ruse 978e7cfac4 wrote a simple test for libs 2022-09-18 06:52:00 +01:00
Kayne Ruse 9c790f8cd6 Got hooks working 2022-09-17 13:01:09 +01:00
Kayne Ruse 7eb16e51bb Changed how string/identifier lengths are determined in copyLiteral 2022-09-15 16:36:42 +01:00
Kayne Ruse bb6e850548 Tweaked build instructions 2022-09-15 11:01:21 +01:00
Kayne Ruse b7f63ea571 Fixed negating identifiers 2022-09-15 09:58:15 +01:00
Kayne Ruse ac37a017bb Fixed astype null 2022-09-14 18:42:33 +01:00
Kayne Ruse 29b1c41064 Implemented #27, easy coercion from int to float 2022-09-14 16:30:54 +01:00
Kayne Ruse 0f6be5ead7 Can no longer use functions as dictionary keys 2022-09-11 13:36:59 +01:00
Kayne Ruse 2d171dd664 Bad mood 2022-09-11 09:12:12 +01:00
Kayne Ruse acb5cc90a9 I can't get the shared lib to build on linux 2022-09-11 18:01:02 +10:00
Kayne Ruse 95ba865cab Packaged toy as a dll 2022-09-11 07:56:06 +01:00
Kayne Ruse 5b420e0799 Changed how parseCompountToPureValue() is called 2022-09-11 05:51:25 +01:00
Kayne Ruse cbc937884e Found a rare lexer bug 2022-09-10 19:11:16 +01:00
Kayne Ruse 60ddd151a6 Patched a leak 2022-09-10 23:14:47 +10:00
Kayne Ruse a7ca6d23d7 Reviewed the spec, fixed some bugs 2022-09-10 14:11:13 +01:00
Kayne Ruse d3c085c300 Added an out-of-bounds check 2022-09-09 19:52:36 +01:00
Kayne Ruse f2443fbde0 Moved type checking into the type checking function 2022-09-09 19:39:05 +01:00
Kayne Ruse 22d0fe596a Tweaked depth check, Resolved #23 2022-09-09 18:10:45 +01:00
Kayne Ruse 4b99ce2fb6 Fixed nested assignment bug 2022-09-09 17:55:23 +01:00
Kayne Ruse f8747d8707 Tried speeding it up, didn't really work 2022-09-08 09:44:45 +01:00
Kayne Ruse a0ea0f7f31 More subtle bugfixes 2022-09-08 05:42:39 +01:00
Kayne Ruse 5861602f23 changed dot operator to access global functions 2022-09-08 01:18:20 +01:00
Kayne Ruse 8550f3141c Fixed bounds error 2022-09-08 04:43:39 +10:00
Kayne Ruse 8d8928438d How did I forget something so basic? 2022-09-07 19:32:43 +01:00
Kayne Ruse 28b1b8e2cc Depth check 2022-09-07 19:00:24 +01:00
Kayne Ruse 9a55ff221a Fixed some indexing bugs 2022-09-07 18:43:32 +01:00
Kayne Ruse 8f61575579 Added to a test 2022-09-07 15:05:46 +01:00
Kayne Ruse 23fdec541d Changed my mind about oftype/typeof 2022-09-07 15:02:17 +01:00
Kayne Ruse 4137b7f057 switched typeof for oftype, switched typeas for astype 2022-09-07 14:47:57 +01:00
Kayne Ruse 6c71a16e3e Fixed type variable evaluation, it now occurs at var definition 2022-09-07 14:21:40 +01:00
Kayne Ruse 6511d652f2 It's bloody never-ending 2022-09-06 20:43:45 +10:00
Kayne Ruse 39af340fbc It's done for now 2022-09-06 10:49:22 +01:00
Kayne Ruse 63a7231254 TWO BYTES 2022-09-06 18:59:25 +10:00
Kayne Ruse d97fd3fc22 More of a sprinkle than a leak 2022-09-06 09:50:14 +01:00
Kayne Ruse 893e435023 Cleaned some leaks 2022-09-06 09:38:02 +01:00
Kayne Ruse 17f1dc8647 cleaning up tests 2022-09-06 09:22:50 +01:00
Kayne Ruse b8f20add66 Finished slice and dot notation, needs a lot of testing 2022-09-06 06:46:50 +01:00
Kayne Ruse 304e1a5fb0 Plumbing for index and dot notations is working 2022-09-05 17:43:42 +01:00
Kayne Ruse 82c03ecb33 Added the typeas keyword to avoid ambiguity 2022-09-05 12:24:42 +01:00
Kayne Ruse 3d8871abe1 Fixed a segfault 2022-09-05 11:23:31 +01:00
Kayne Ruse 2aecf6e8a1 Implemented typeof and added resetInterpreter() 2022-09-05 10:56:35 +01:00
Kayne Ruse 2a3206d951 Rearranged some internal initialization to support multiple files being run 2022-09-05 09:55:04 +01:00
Kayne Ruse f80709ae41 Solved a function scope issue 2022-09-05 16:39:09 +10:00
Kayne Ruse 33302ef318 Another leak 2022-09-05 06:53:53 +01:00
Kayne Ruse 9ce5f6e67e Missed a memory leak 2022-09-05 06:40:48 +01:00
Kayne Ruse 7fb9ebbce0 Import and export are working 2022-09-05 06:39:05 +01:00
Kayne Ruse dceb83e618 Suppressed print statements during tests 2022-09-05 05:30:06 +01:00
Kayne Ruse fb1ac71f42 Resolved #18 2022-09-05 03:35:17 +01:00
Kayne Ruse 54aacff26e Missed a couple 2022-09-04 15:29:57 +01:00
Kayne Ruse ae270008b0 Added errorOutput to interpreter, moved builtin functions to their own file 2022-09-04 15:15:30 +01:00
Kayne Ruse 22af1edb1d So apparently, casting doens't need parens 2022-09-04 14:08:42 +01:00
Kayne Ruse ac35a859e0 Fixed a leak 2022-09-04 20:23:50 +10:00
Kayne Ruse cc27da50df Reworked dot.toy into a proper test 2022-09-04 10:37:19 +01:00
Kayne Ruse 6787cfff55 Tested matrix manipulation 2022-09-04 10:27:16 +01:00
Kayne Ruse 36154b25ac Changed dict type syntax to using a colon instead of a comma 2022-09-04 09:01:09 +01:00
Kayne Ruse d12ad4f60d Fixed codeStart issue 2022-09-04 08:51:19 +01:00
Kayne Ruse 86061cb74e Fixed an argument list bug, found a casting bug 2022-09-03 16:04:18 +01:00
Kayne Ruse 32d6b7124c All leaks plugged for the time being 2022-09-03 19:16:13 +10:00
Kayne Ruse c039045c14 Progress 2022-09-03 09:47:08 +01:00
Kayne Ruse e6fe42c0ca Getting sleepy 2022-09-03 09:20:21 +01:00
Kayne Ruse 672d68a73f Tweak 2022-09-03 09:18:41 +01:00
Kayne Ruse 1b8559f0ef Squashing bugs 2022-09-03 08:45:06 +01:00
Kayne Ruse 4ad33a3082 Fixed jumps in functions issue 2022-09-03 07:57:25 +01:00
Kayne Ruse d2aacea8c5 This is really getting hard 2022-09-03 11:58:36 +10:00
Kayne Ruse 6c151e21b0 tired 2022-09-03 06:58:50 +10:00
Kayne Ruse 4625efecfd Fixed native function issues 2022-09-02 21:04:23 +01:00
Kayne Ruse c58c8911fe Going well tonight - need a break 2022-09-03 00:51:55 +10:00
Kayne Ruse 1f6b3e232d I feel like there's just a few bugs left 2022-09-02 05:47:48 +10:00
Kayne Ruse 90d18c7520 fixed the fix 2022-09-02 04:03:40 +10:00
Kayne Ruse ea3ca2751c Fixed failing printing of compound types 2022-09-01 18:56:28 +01:00
Kayne Ruse 90b504d3a4 No segfaults, still failing tests 2022-09-01 14:00:31 +10:00
Kayne Ruse 3a4017cea5 Lots of runtime errors 2022-08-30 12:09:11 +10:00
Kayne Ruse 14718a08d3 I fucking found it 2022-08-30 04:51:28 +10:00
Kayne Ruse 1c6c7e5d4f I GIVE UP 2022-08-29 21:38:54 +10:00
Kayne Ruse 08c8e7e3e6 Another one bites the dust! 2022-08-29 16:25:53 +10:00
Kayne Ruse d055e9dc94 WE ARE THE CHAMPIONS! 2022-08-29 15:33:58 +10:00
Kayne Ruse a6f0200255 Eventually gonna squash all of today's work 2022-08-29 04:08:31 +01:00
Kayne Ruse 61efb96fe2 Don't get testy with me 2022-08-29 12:23:48 +10:00
Kayne Ruse 1937d727bb Working on refactoring, read more
The main program segfaults, but right now I'm working on the tests, mainly.
2022-08-29 10:21:25 +10:00
Kayne Ruse 4fb2dea1b4 Found a leak, moving to linux 2022-08-28 23:47:58 +01:00
Kayne Ruse f705d82aee Wrote some small tests 2022-08-28 09:35:46 +01:00
Kayne Ruse 71ff481f6c Began working on unit tests 2022-08-28 08:10:41 +01:00
Kayne Ruse 5300e2ceec Gonna start unit testing to resolve issues 2022-08-28 07:03:12 +01:00
Kayne Ruse 9c766ec61e Working on memory issues 2022-08-28 01:35:00 +01:00
Kayne Ruse 3355c3a4c2 Forgot printing native functions 2022-08-27 15:41:21 +01:00
Kayne Ruse ec91bac8a5 Native functions are working 2022-08-27 15:39:42 +01:00
Kayne Ruse e523a6f60a Interpreter adopts inner interpreter panic state on assert failure 2022-08-27 12:07:41 +01:00
Kayne Ruse 90e5a5d08b Rest parameter working 2022-08-27 11:59:54 +01:00
Kayne Ruse 85f3dfbf1d TODO tweak 2022-08-26 13:04:37 +01:00
Kayne Ruse 151f681954 calls within parameter lists 2022-08-26 13:03:18 +01:00
Kayne Ruse 7bd67765aa Functions take a set number of arguments 2022-08-26 12:48:10 +01:00
Kayne Ruse f36289786e Fixed a repl bug 2022-08-26 11:59:09 +01:00
Kayne Ruse ffc50ceafb Closures work 2022-08-26 03:54:58 +01:00
Kayne Ruse 0c67ce6476 Stopgap limit on return count 2022-08-26 02:14:05 +01:00
Kayne Ruse a4f1e048e9 Function calls, with args and single returns are working 2022-08-26 01:48:26 +01:00
Kayne Ruse 041fe99e01 Function declaration seems to work 2022-08-25 15:19:08 +01:00
Kayne Ruse 4f70bea808 Implemented logical && and || 2022-08-23 05:07:17 +01:00
Kayne Ruse 6939b216a9 Added arithmetic assign operators 2022-08-23 03:33:28 +01:00
Kayne Ruse ce2073832b Patched a potential leak 2022-08-22 21:23:24 +01:00
Kayne Ruse 08e2adab50 #15 Fixed some of the worst memory leaks 2022-08-22 20:51:09 +01:00
Kayne Ruse b675c4c1bd Merge pull request #17 from lang-viper/0.6.0
Fix heap use after free
2022-08-23 04:57:11 +10:00
solar-mist ae32a2e7ae Fix heap use after free in compiler.c 2022-08-22 18:48:44 +00:00
solar-mist 2f538e79fe Fix heap use after free in compiler.c 2022-08-22 18:48:08 +00:00
Kayne Ruse ebe1d712c9 Null values are always allowed 2022-08-22 18:26:05 +01:00
Kayne Ruse 5b82ed8e45 Must force a type when dealing with compound types 2022-08-22 14:27:41 +01:00
Kayne Ruse 300f8e382b Store complex types in variables 2022-08-22 13:28:44 +01:00
Kayne Ruse 45920f763c Tweaked error messages 2022-08-22 05:17:17 +01:00
Kayne Ruse 0174deb08a Resolved #16 2022-08-22 04:45:08 +01:00
Kayne Ruse 9ac16f8e80 Comment tweaks 2022-08-22 02:16:24 +01:00
Kayne Ruse df6bd58b1a Types are first-class citizens 2022-08-22 01:59:29 +01:00
Kayne Ruse 9c995830e2 Resolved #14 2022-08-21 00:51:28 +01:00
Kayne Ruse dc1914b9ed Resolved #13 2022-08-21 00:36:05 +01:00
Kayne Ruse b385b461e0 Increment and decrement operators work 2022-08-21 00:23:09 +01:00
Kayne Ruse c64d451287 Added string concatenation 2022-08-20 22:58:50 +01:00
Kayne Ruse 80ccd119ff Changed truthiness 2022-08-20 22:43:44 +01:00
Kayne Ruse 18ecece348 Tweaked TODO 2022-08-20 20:11:11 +01:00
Kayne Ruse 18c5fb6add Break and continue keywords are working 2022-08-20 20:08:22 +01:00
Kayne Ruse daceaa5492 Found a weird bug, reporting it 2022-08-20 13:44:55 +01:00
Kayne Ruse f5e060051e While and for loops are working 2022-08-20 13:27:47 +01:00
Kayne Ruse 975ed41d14 If-then-else is working with jump statements 2022-08-20 12:38:29 +01:00
Kayne Ruse cbdfcbcc14 Tweaked docs 2022-08-20 07:21:43 +01:00
Kayne Ruse 8309535bbe Fixed negation issue, moved some scripts to test/ 2022-08-20 07:20:29 +01:00
Kayne Ruse 2bf721867b Comparions and the ! operator work 2022-08-20 06:20:20 +01:00
Kayne Ruse 8eca419138 Updated spec 2022-08-19 20:11:56 +01:00
Kayne Ruse 596a4882bc Fixed a print bug with casting 2022-08-19 19:53:55 +01:00
Kayne Ruse 97e72550da Removed under-used optimization options 2022-08-19 19:27:23 +01:00
Kayne Ruse 4f0aebc32f Type casting is working: 2022-08-19 19:24:07 +01:00
Kayne Ruse 9365541c8f Removed extra parameter 2022-08-19 17:56:35 +01:00
Kayne Ruse 16291bd1af Declare variables with a type but no value allowed 2022-08-19 17:52:24 +01:00
Kayne Ruse 574a864dd0 Reworked the type system, types now have a single value 2022-08-17 17:39:33 +01:00
Kayne Ruse bde07f6c12 Removed debug.c and debug.h, since they're outmoded 2022-08-15 13:08:31 +01:00
Kayne Ruse 42716b24c9 Updated README.md 2022-08-15 12:29:27 +01:00
Kayne Ruse ecca17dbef Updated spec 2022-08-15 12:17:27 +01:00
Kayne Ruse 7e01ead6ff Fixed some issues in scope 2022-08-15 10:15:24 +01:00
Kayne Ruse 6b834ca6d1 Type checking in place 2022-08-15 03:52:44 +01:00
Kayne Ruse 8d12db7dbe Some declaration guards 2022-08-15 03:12:10 +01:00
Kayne Ruse 4cda75df11 Added a limit to string and identifier lengths 2022-08-15 02:53:30 +01:00
Kayne Ruse 9e899f5974 Got scope-based variable shadowing working 2022-08-14 21:32:13 +01:00
Kayne Ruse 4aa6f75ea7 Expression statements and assignemnts are working 2022-08-14 19:57:24 +01:00
Kayne Ruse e9ab6f3f96 Variables now persist between statements 2022-08-13 21:27:39 +01:00
Kayne Ruse 633df5f376 Tweaked example.toy 2022-08-13 16:34:07 +01:00
Kayne Ruse 74e2907f09 Fixed array/dictionary confusion 2022-08-13 16:31:11 +01:00
Kayne Ruse 3098d75d01 Caught some loose memory 2022-08-13 16:26:51 +01:00
Kayne Ruse b80888a7bb Removed debugging output 2022-08-13 15:57:51 +01:00
Kayne Ruse afad0d99cb I think var declarations are working correctly; no type checking yet 2022-08-13 15:57:30 +01:00
Kayne Ruse 55aa6eb273 The value is displaying correctly, but not the type 2022-08-12 17:27:57 +01:00
Kayne Ruse 7424a681d6 Fixed a segfault 2022-08-12 15:14:21 +01:00
Kayne Ruse 53f0996fd1 Types are being read into the variable declaration, not yet used 2022-08-12 15:06:41 +01:00
Kayne Ruse 998b913fc9 Got literal types represented correctly 2022-08-12 11:10:52 +01:00
Kayne Ruse 2f18989f25 Types now exist, but they're not being embedded into bytecode yet 2022-08-12 09:11:43 +01:00
Kayne Ruse 80b64cf21e Fixed division by zero bugs 2022-08-12 07:26:58 +01:00
Kayne Ruse 42e55c1db6 Added equality check for arrays and dictionaries - needs testing 2022-08-11 20:13:01 +01:00
Kayne Ruse 00812e7a73 pre-computed identifer hashes 2022-08-11 18:36:03 +01:00
Kayne Ruse 1430aefdf3 Bugfix when strings are unterminated 2022-08-11 14:01:33 +01:00
Kayne Ruse 46097000a8 Removed debug display 2022-08-11 13:50:24 +01:00
Kayne Ruse 5c93bf663a Tweaked string quotes 2022-08-11 13:45:13 +01:00
Kayne Ruse 32ac46d9a0 Fiddled with the .toy file 2022-08-11 12:05:04 +01:00
Kayne Ruse 52651c5f2c Tweaked dictionary hashing to not hash null keys 2022-08-11 11:38:27 +01:00
Kayne Ruse 603d9d2b06 Recursive arrays and dictionaries! 2022-08-11 09:24:13 +01:00
Kayne Ruse 380b7a3699 fixed a bug 2022-08-10 18:22:02 +01:00
Kayne Ruse 67f7b3e436 Arrays and dictionaries have been implemented, read more
The arrays and dictionaries are currently being printed out correctly,
afaik. This means I should be able to go back and work on the type
system, assuming nothing happens.
2022-08-10 17:42:04 +01:00
Kayne Ruse 6a883bde96 Parser is reading variable declarations, read more
This is an incomplete process. It's supposed to be robust enough to
support the types of arrays and dictionaries, but arrays and
dictionaries aren't implemented in the literals yet, so that's my next
task.

I'll come back to variable declarations later.
2022-08-10 11:01:32 +01:00
Kayne Ruse 9603baeb0a Added console colors, tweaked help menu 2022-08-09 13:51:03 +01:00
Kayne Ruse 6d5549fc8e Added scopes using '{}' symbols, read more
I've also added a new literal type called 'identifier'. This will be
used for variable names, and has a type mask embedded in it.
2022-08-08 15:02:12 +01:00
Kayne Ruse 08ce270e06 Minor tweak to header loading 2022-08-08 10:35:41 +01:00
Kayne Ruse cf8b3975c0 Tinkering with the repl 2022-08-08 09:39:40 +01:00
Kayne Ruse 6f4bfc0e10 Implemented and tested literal dictionary 2022-08-08 07:58:30 +01:00
Kayne Ruse 3fb952e3c2 Minor tweaks 2022-08-08 03:42:48 +01:00
Kayne Ruse ceeca9d40f Fiddling with -Wpedantic 2022-08-07 21:39:21 +01:00
Kayne Ruse 3b89e216cc Fixed an non-optimized grouping bug 2022-08-07 16:46:54 +01:00
Kayne Ruse 072d9c59cc Fixed readme 2022-08-07 15:09:06 +01:00
Kayne Ruse 9a415738d9 Implemented groupings 2022-08-07 15:04:19 +01:00
Kayne Ruse d7fda480fd Added assert keyword, with re-routable print and assert outputs 2022-08-07 12:26:04 +01:00
Kayne Ruse 7ff232c814 Removed a trace statement 2022-08-06 14:52:59 +01:00
Kayne Ruse a05b93975e Basic arithmetic works 2022-08-06 14:50:22 +01:00
Kayne Ruse c178824a0a Forgot a function call 2022-08-06 08:22:09 +01:00
Kayne Ruse 3cdf77b85c Repl working 2022-08-06 08:16:54 +01:00
Kayne Ruse 7a3986af33 Wrote the interpreter 2022-08-06 07:58:32 +01:00
Kayne Ruse 0048c92cf5 Adjusted some includes 2022-08-06 06:16:14 +01:00
Kayne Ruse 6b33895f75 Added optimisation levels 2022-08-06 05:59:29 +01:00
Kayne Ruse 577d1965cb Fixed a missing string terminator 2022-08-05 17:14:33 +01:00
Kayne Ruse bfaf4e83bb Fixed a string comparison bug 2022-08-05 17:11:21 +01:00
Kayne Ruse 8f2ba5cd50 Tested long literals, fixed a dissection bug 2022-08-05 16:47:09 +01:00
Kayne Ruse cd05d5d84a Got the compiler partially working 2022-08-05 16:29:12 +01:00
Kayne Ruse 1ff32fe101 Lexer partially working 2022-08-03 14:06:54 +01:00
Kayne Ruse 3cad70dddd Wrote a basic lexer 2022-08-03 09:35:20 +01:00
Kayne Ruse 3cbf7b13eb Initial commit 2022-08-03 07:39:50 +01:00
222 changed files with 14897 additions and 18613 deletions
-29
View File
@@ -1,29 +0,0 @@
---
name: Bug Report
about: Create a report to help us improve
labels: bug
---
## Describe the bug
A clear and concise description of what the bug is.
## To Reproduce
Steps to reproduce the behaviour:
1. run `git pull` on the repository
2. run `make rebuild` on the code
3. ...
You can include some screenshots here if you'd like!
## Versioning
- OS: [for example MacOS, Windows, iOS, Android]
- Version: [What version of Toy was this running?]
### Additional context
Add any other context about the problem here.
-17
View File
@@ -1,17 +0,0 @@
---
name: Feature Request
about: Suggest an idea
labels: enhancement
---
### Describe the feature youd like
A clear and concise description of what youd like to be able to do with Toy.
### Describe alternatives you've considered
A clear and concise description of any alternative solutions or workarounds you've considered.
### Additional context
Add any other context about the feature request here.
-10
View File
@@ -1,10 +0,0 @@
---
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.
+19
View File
@@ -0,0 +1,19 @@
name: Comprehensive Tests
on:
push:
branches: [ "main", "dev" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: install valgrind
run: sudo apt install valgrind
- name: make test
run: make test
@@ -1,42 +0,0 @@
name: Continuous Integration v2.x
#trigger when these occur
on:
push:
branches:
- v2
pull_request:
types:
- opened
- edited
- reopened
branches:
- v2
workflow_dispatch:
jobs:
#CI workflows across all supported platforms
standard:
strategy:
fail-fast: false
matrix:
platforms:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.platforms }}
steps:
- uses: actions/checkout@v6
- name: Run all tests
run: make tests
gdb:
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install GDB if not present
run: sudo apt update && sudo apt install gdb
- name: Run all tests under gdb
run: make tests-gdb
+25 -51
View File
@@ -1,57 +1,31 @@
# Prerequisites
*.d
#Editor generated files
*.sln
*.vcproj
*.suo
*.ncb
*.user
compile_commands.json
# Object files
#Directories
Release/
Debug/
Out/
release/
debug/
out/
.cache/
#Project generated files
*.db
*.o
*.ko
*.obj
*.elf
# 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
*.meta
*.log
out
*.stackdump
*.tb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
#mdbook files
book
mdbook
#Shell files
*.bat
*.sh
-2
View File
@@ -1,2 +0,0 @@
This folder is full of development notes, and are probably out of date. Check the actual docs for the correct info.
-152
View File
@@ -1,152 +0,0 @@
This file is messy and confusing, and makes sense to nobody but me - so don't worry about understanding it too much - better docs will come later.
===
SECD = State, Environment, Control, Dump
The idea of "Landin's SECD Machine" is to store the working memory in S, the variable-value bindings in E, the code/instructions in C, and the program stack in D.
Notes:
DEFINE = DECLARE + SET
The environment, denoted with an E, is created on routine start, and destroyed on routine end - however, it uses the parent routine's environment as the starting point for it's creation, so closures work as expected
unlike version 1, identifiers are not a valid datatype - they're just an index representing a symbol, like "standard::clock"
meta opcodes - EOF, PASS, ERROR,
a "value" can be of any valid datatype, and may point to various parts of memory to define it's value
Symbols will be awkward... I suspect the symbol table might need to be rebuilt on startup, as the order of the modules will not necessarily be the same each time
The various instances of S could be the same array in memory, simply marked as "unused"? You could stick C on there as a value before "pushing" for a new routine
Things to consider later:
type cast?
rest parameter?
index access and assign?
===
//variable instructions
READ
read one value from C onto S
LOAD
read one value from .data onto S
DECLARE
read two words from C, create a new entry in E with the key E[SYMBOL(word1)], the type defined by word2, the value 'null'
DEFINE
read one word from C, saves the pre-existing key E[SYMBOL(word)] to the value S(0), popping S(0)
ACCESS
read one word from C, finds the pre-existing value of E[SYMBOL(word)], leaves the value on S
//arithmetic instructions
ADD
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
SUBTRACT
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
MULTIPLY
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
DIVIDE
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
MODULO
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
//comparison instructions
COMPARE_EQUAL
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on equality
COMPARE_LESS
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
COMPARE_LESS_EQUAL
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
COMPARE_GREATER
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
COMPARE_GREATER_EQUAL
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
//logical instructions
AND
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
OR
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
TRUTHY
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
NEGATE
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
//control instructions
JUMP
read one value from C, and move the program counter to that location (relative to the current position)
JUMP_IF_FALSE
read one value from C, pops S(0), and move the program counter to that location (relative to the current position) if the popped value is falsy
FN_CALL
*read a list of arguments specified in C into 'A', store (S, E, C, D) as D, push S, move the stack pointer to the specified routine, push a new E based on the contents of 'A'
FN_RETURN
*read a list of return values specified in C into 'R', pop S, restore (S, E, C, D) from D(0) popping it, store the contents of 'R' in E or S based on the next few parts of C
//various action instructions
ASSERT
if S(-1) is falsy, print S(0) and exit
PRINT
pop S(0), and print the output
IMPORT
//invoke an external library into the current scope
CONCAT
//combine two strings
SCOPE_BEGIN
//push an inner environment to E, which should be automatically popped at the routine's end
SCOPE_END
//pop an inner environment from E, only if it was created with SCOPE_BEGIN
===
FN_CALL
read word: read the following N arguments
for 0 to N do:
read word as match: # this allows literals and identifiers as arguments
stack: then pop S(0) into 'A'
**env: then read word, load E[SYMBOL(word)] into 'A'
read word:
determine where the routine is (is it new or is it a value?) and hold it for a moment
push E and C into a frame marker on S
jump C to the routine
read word:
read the following N parameter names, storing each member of 'A' as their value in E[SYMBOL(name)]
continue
FN_RETURN
read word: read the following N return values
for 0 to N do:
read word as match: # this allows literals and identifiers as arguments
stack: then pop S(0) into 'R'
**env: then read word, load E[SYMBOL(word)] into 'R'
pop E and S
extract and restore E and C from the frame marker on S
read word: read the following N storage locations for the values within `R`
for 0 to N do:
read word as match: # you're effectively reversing the prior reads
stack: then push from 'R' onto S
**env: then read word, save 'R' into E[SYMBOL(word)]
**This could work by listing the sources as e.g. "SSSExS" - three stacks and one environment variable loaded onto the stack, then one more stack for a total of four values
Notes:
the bytecode of a funtion call would look like:
FN_CALL N [stack|env word]... N [stack|env word]...
the value of C stored in D points to the second N, while it waits to pick up where it left off
===
-64
View File
@@ -1,64 +0,0 @@
The bytecode format
===
NOTE: This datestamp header is currently not implemented
There are four components in the datestamp header:
TOY_VERSION_MAJOR
TOY_VERSION_MINOR
TOY_VERSION_PATCH
TOY_VERSION_BUILD
The first three are each one unsigned byte, and the fourth is a null terminated C-string.
* Under no circumstance, should you ever run bytecode whose major version is different
* Under no circumstance, should you ever run bytecode whose minor version is above the interpreters minor version
* You may, at your own risk, attempt to run bytecode whose patch version is different from the interpreters patch version
* You may, at your own risk, attempt to run bytecode whose build version is different from the interpreters build version
An additional note: The contents of the build string may be anything, such as:
* the compilation date and time of the interpreter
* a marker identifying the current fork and/or branch
* identification information, such as the developer's copyright
* a link to Risk Astley's "Never Gonna Give You Up" on YouTube
Please note that in the final bytecode, if the null terminator of TOY_VERSION_BUILD is not 4-byte aligned, extra space will be allocated to round out the header's size to a multiple of 4. The contents of the extra bytes are undefined.
===
Bytecode Format Structure
.header:
N total size # size of this routine, including all data and subroutines
N .jumps count # the number of entries in the jump table (should be data count + routine count)
N .param count # the number of parameter fields expected (a secondary jump table, used for subroutine parameters)
N .data count # the number of data fields present
N .subs count # the number of subroutines present
.code start # absolute address of .code; mandatory
.param start # absolute addess of .param; omitted if not needed
.datatable start # absolute address of .datatable; omitted if not needed
.data start # absolute address of .data; omitted if not needed
.subs start # absolute address of .subs; omitted if not needed
# additional metadata fields can be added later
.code:
# opcode instructions read and 'executed' by the interpreter (aligned to 4-byte widths)
[READ, TOY_VALUE_STRING, Toy_StringType, stringLength] [jumpIndex]
.jumps:
# a layer of indirection for quickly looking up values in .data and .subs
0 -> {string, 0x00}
4 -> {fn, 0xFF}
.param:
# a list of names, stored in .data, to be used for any provided function arguments
.data:
# data that can't be cleanly embedded into .code, such as strings
"Hello world\0"
.subs:
# an extension of .data, used exclusively for subroutines (they also follow this spec, recursively)
-23
View File
@@ -1,23 +0,0 @@
#include <stdio.h>
#include <stdint.h>
uint32_t hash (uint32_t x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x);
return x;
}
uint32_t unhash ( uint32_t x ) {
x = (( x >> 16) ^ x) * 0x119de1f3;
x = (( x >> 16) ^ x) * 0x119de1f3;
x = (( x >> 16) ^ x);
return x;
}
int main() {
//I legit didn't know this algorithm could be reversed. Neat.
uint32_t value = 42;
printf("%u %u %u", value, hash(value), unhash(hash(value)));
return 0;
}
-8
View File
@@ -1,8 +0,0 @@
The default version of GCC that ships on Raspian has an issue. The file '/usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so' has a faulty implementation of 'memcpy()' and possibly 'memset()'. Changing to the newer versions doens't work, as they're just symlinks to v7.
To resolve this, download and build this shared object:
https://github.com/simonjhall/copies-and-fills
Then, set the linker's preload value to point to that '.so' file (You may need to edit '/etc/ld.so.preload')
-5
View File
@@ -1,5 +0,0 @@
{
"recommendations": [
"gruntfuggly.todo-tree"
]
}
-66
View File
@@ -1,66 +0,0 @@
{
"todo-tree.filtering.includeGlobs": [
"**/repl/**",
"**/scripts/**",
"**/source/**",
"**/tests/**",
"**/tools/**",
],
"todo-tree.filtering.excludeGlobs": [
"**/obj/**",
"**/out/**",
],
"todo-tree.general.tags": [
"URGENT",
"BUG",
"TODO",
"WARN",
"BUGFIX",
"WONTFIX",
"NOTE"
],
"todo-tree.highlights.customHighlight": {
"URGENT": {
"icon": "alert",
"type": "text",
"iconColour": "#FF0000",
"foreground": "#FF0000"
},
"BUG": {
"icon": "bug",
"type": "text",
"iconColour": "#FF0000",
"foreground": "#FF0000"
},
"TODO": {
"icon": "alert",
"type": "text",
"iconColour": "#FFFF00",
"foreground": "#FFFF00"
},
"WARN": {
"icon": "alert",
"type": "text",
"iconColour": "#FFA500",
"foreground": "#FFA500"
},
"BUGFIX": {
"icon": "bug",
"type": "text",
"iconColour": "#00A000",
"foreground": "#00A000"
},
"WONTFIX": {
"icon": "bug",
"type": "text",
"iconColour": "#B64949",
"foreground": "#B64949"
},
"NOTE": {
"icon": "alert",
"type": "text",
"iconColour": "#00A000",
"foreground": "#00A000"
},
}
}
-1
View File
@@ -1 +0,0 @@
No hating on other people, OK?
-17
View File
@@ -1,17 +0,0 @@
Copyright (c) 2020-2026 Kayne Ruse, KR Game Studios
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+13
View File
@@ -0,0 +1,13 @@
# License
Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+48 -52
View File
@@ -1,75 +1,71 @@
<p align="center">
<image src="toylogo.png" alt="The Toy Logo" />
<image src="toylogo.png" />
</p>
# Toy v2.x
# Toy
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through external scripts.
This is the Toy programming language interpreter, written in C.
This repository holds the reference implementation for Toy version 2.x, written in C - alpha testing is currently underway.
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
# Nifty Features
* Simple C-like syntax
* Intermediate AST and bytecode representations
* Strong, but optional type system
* First-class functions and closures
* Extensible with native C-bindings
* Can re-direct output, error and assertion messages
* Open-Source under the zlib license
* Bytecode intermediate compilation
* Optional, but robust type system (including `opaque` for arbitrary data)
* Functions and types are first-class citizens
* `import` and `export` variables from the host program
* Fancy slice notation for strings, arrays and dictionaries
* Can re-direct output, error and assertion failure messages
* Open source under the zlib license
# Syntax
## Building
```toy
fn makeCounter() {
var counter: Int = 0;
For Windows, Linux and MacOS, simply run `make` in the root directory.
fn increment() {
return ++counter;
Note: For Linux, you may need to `cd` into the `out` directory before running.
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
## Tools
Run `make install-tools` to install a number of tools, including:
* VSCode syntax highlighting
## Syntax
```
import standard; //for a bunch of utility functions
print "Hello world"; //"print" is a keyword
var msg = "foobar"; //declare a variable like this
assert true, "This message won't be seen"; //assert is another keyword
//-------------------------
fn makeCounter() { //declare a function like this
var total: int = 0; //declare a variable with a type like this
fn counter(): int { //declare a return type like this
return ++total;
}
return increment;
return counter; //closures are explicitly supported
}
var tally = makeCounter();
while (true) {
var result = tally();
print tally(); //1
print tally(); //2
print tally(); //3
print result; //prints 1 to 10
if (result >= 10) {
break;
}
}
export tally; //export this variable to the host program
```
# Building
This project requires `gcc` and `make` by default, but should also work in other environments. Officially supported platforms include `linux`, `windows` and `macOS`, see `source/toy_common.h` for implementation details.
Run `make` in the root directory to build the shared library named `libToy.so` and a useable REPL named `repl.out`.
# Documentation
The contents of `docs/` is also available on the official website [toylang.com](https://toylang.com/).
# License
This source code is covered by the Zlib license (see [LICENSE](LICENSE) for details).
# Contributors and Special Thanks
@NishiOwO - Unofficial NetBSD support
@Gipson62 - v1 docs spell checking
@8051Enthusiast - `fixAlignment()` trick
@hiperiondev - v1 Disassembler, v1 porting support and feedback
@add00 - v1 Library support
@gruelingpine185 - Unofficial v1 MacOS support
@solar-mist - v1 Minor bugfixes
Various Anons - Feedback
@munificent - For [writing the book](http://craftinginterpreters.com/) that sparked my interest in langdev
# Patreon Supporters
You can show your support and be listed here by joining my [Patreon](https://patreon.com/krgamestudios).
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
-9
View File
@@ -1,9 +0,0 @@
[book]
title = "The Toy Programming Language"
authors = ["Kayne Ruse (Ratstail91)"]
description = "Documentation For The Toy Programming Language"
language = "en"
[output.html]
git-repository-url = "https://github.com/krgamestudios/Toy"
git-repository-icon = "fab-github"
-3
View File
@@ -1,3 +0,0 @@
# 404
Nobody here but us chickens!
-47
View File
@@ -1,47 +0,0 @@
<p align="center">
<image src="img/toylogo.png" alt="The Toy Logo" />
</p>
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through external scripts.
## Nifty Features
* Simple C-like syntax
* Intermediate AST and bytecode representations
* Strong, but optional type system
* First-class functions and closures
* Extensible with native C-bindings
* Can re-direct output, error and assertion messages
* Open-Source under the zlib license
## Syntax
```toy
fn makeCounter() {
var counter: Int = 0;
fn increment() {
return ++counter;
}
return increment;
}
var tally = makeCounter();
while (true) {
var result = tally();
print result; //prints 1 to 10
if (result >= 10) {
break;
}
}
```
## Further Reading
This website is a work in progress - for further info, see the official repository: [https://gitea.krgamestudios.com/krgamestudios/Toy](https://gitea.krgamestudios.com/krgamestudios/Toy), or the GitHub mirror: [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
An example of Toy in action: [Vampire Toyvivors](https://gitea.krgamestudios.com/krgamestudios/VampireToyvivors) (a simple "game" used for testing).
-5
View File
@@ -1,5 +0,0 @@
# Summary
- [Front Page](./README.md)
- [Quick Start](./quickstart.md)
- [Cheat Sheet](./cheatsheet.md)
-144
View File
@@ -1,144 +0,0 @@
# Cheat Sheet
## Compile and Run A Snippet Of Code
```c
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_vm.h"
#include <stdlib.h>
int main() {
//example code
const char* source = "print \"Hello world!\";";
//buckets use the arena pattern for memory allocation
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
//compile the code
Toy_Lexer lexer;
Toy_bindLexer(&lexer, (char*)source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
unsigned char* bytecode = Toy_compileToBytecode(ast);
//the ast, which is stored in this bucket, is no longer needed
Toy_freeBucket(&bucket);
//the virtual machine used at runtime
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, bytecode, NULL);
//execute the given code
Toy_runVM(&vm);
//cleanup after ourselves
Toy_freeVM(&vm);
free(bytecode);
}
```
## Quick and Dirty Compilation
```c
unsigned char* compileSource(const char* source) {
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Lexer lexer;
Toy_bindLexer(&lexer, source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
unsigned char* bytecode = Toy_compileToBytecode(ast);
Toy_freeBucket(&bucket);
return bytecode;
}
```
## API Functions
This is a rough outline of all API functions declared in Toy's headers. As a rule, functions that begin with `TOY_API` are useable and begin with `Toy_`, while functions that begin with `Toy_private_` are generally not intended for use, and only exposed for technical reasons.
*Note: This list is updated manually, if something is outdated let me know.*
```c
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
TOY_API void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb);
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
TOY_API Toy_Bucket* Toy_allocateBucket(unsigned int capacity);
TOY_API unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount);
TOY_API void Toy_releaseBucketPartition(unsigned char* ptr);
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
TOY_API unsigned char* Toy_compileToBytecode(Toy_Ast* ast);
TOY_API void Toy_freeFunction(Toy_Function* fn) {
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope);
TOY_API Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback);
TOY_API Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* fn);
TOY_API void Toy_freeFunction(Toy_Function* fn);
TOY_API void Toy_bindLexer(Toy_Lexer* lexer, const char* source);
TOY_API void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer);
TOY_API Toy_Ast* Toy_scanParser(Toy_Bucket** bucketHandle, Toy_Parser* parser);
TOY_API void Toy_resetParser(Toy_Parser* parser);
TOY_API void Toy_print(const char* msg);
TOY_API void Toy_error(const char* msg);
TOY_API void Toy_assertFailure(const char* msg);
TOY_API void Toy_setPrintCallback(Toy_callbackType cb);
TOY_API void Toy_setErrorCallback(Toy_callbackType cb);
TOY_API void Toy_setAssertFailureCallback(Toy_callbackType cb);
TOY_API void Toy_resetPrintCallback(void);
TOY_API void Toy_resetErrorCallback(void);
TOY_API void Toy_resetAssertFailureCallback(void);
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant);
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
TOY_API Toy_Stack* Toy_allocateStack(void);
TOY_API void Toy_freeStack(Toy_Stack* stack);
TOY_API void Toy_resetStack(Toy_Stack** stackHandle);
TOY_API void Toy_pushStack(Toy_Stack** stackHandle, Toy_Value value);
TOY_API Toy_Value Toy_peekStack(Toy_Stack** stackHandle);
TOY_API Toy_Value Toy_popStack(Toy_Stack** stackHandle);
TOY_API Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring);
TOY_API Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
TOY_API Toy_String* Toy_copyString(Toy_String* str);
TOY_API Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right);
TOY_API void Toy_freeString(Toy_String* str);
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
TOY_API char* Toy_getStringRaw(Toy_String* str);
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right);
TOY_API unsigned int Toy_hashString(Toy_String* string);
TOY_API Toy_Table* Toy_allocateTable(unsigned int minCapacity);
TOY_API void Toy_freeTable(Toy_Table* table);
TOY_API void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value);
TOY_API Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key);
TOY_API void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key);
TOY_API Toy_Value Toy_unwrapValue(Toy_Value value);
TOY_API unsigned int Toy_hashValue(Toy_Value value);
TOY_API Toy_Value Toy_copyValue(struct Toy_Bucket** bucketHandle, Toy_Value value);
TOY_API void Toy_freeValue(Toy_Value value);
TOY_API bool Toy_checkValueIsTruthy(Toy_Value value);
TOY_API bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right);
TOY_API bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right);
TOY_API int Toy_compareValues(Toy_Value left, Toy_Value right);
TOY_API union Toy_String_t* Toy_stringifyValue(struct Toy_Bucket** bucketHandle, Toy_Value value);
TOY_API const char* Toy_getValueTypeAsCString(Toy_ValueType type);
TOY_API void Toy_resetVM(Toy_VM* vm, bool preserveScope, bool preserveStack);
TOY_API void Toy_initVM(Toy_VM* vm);
TOY_API void Toy_inheritVM(Toy_VM* parentVM, Toy_VM* subVM);
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
TOY_API void Toy_freeVM(Toy_VM* vm);
TOY_API Toy_Value Toy_getReturnValueFromVM(Toy_VM* parentVM, Toy_VM* subVM);
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 KiB

-186
View File
@@ -1,186 +0,0 @@
# Toy v2 Quick-Start Guide
To help you start using Toy as fast as possible, here are the most useful elements of the language. Not everything available is listed, but this should let you start coding right away.
## Keyword 'print'
The `print` keyword prints a given value to stdout (or elsewhere if configured with the API).
```
print "Hello World!";
```
## Keyword 'assert'
The `assert` keyword takes two values separated by a comma. If the first value is falsy or `null` the optional second parameter is printed to stderr (or elsewhere if configured with the API). If no second parameter is provided a generic error message is used instead.
```
//nothing happens
assert 1 < 2;
//this assert will fail, and output the second parameter
assert null, "Hello world!";
```
## Variables and Types
Variables are declared with the `var` keyword with and an optional type from the list below. If no type is specified `Any` is used instead.
```
var answer = 42;
var question: String = "How many roads must a man walk down?";
```
To make a variable immutable put the `const` keyword after the type. If you do, it must be assigned a value.
```
var quote: String const = "War. War never changes.";
```
Toy's types are:
| type | name | description |
| --- | --- | --- |
| `Bool` | Boolean | Either `true` or `false`. |
| `Int` | Integer | Any signed whole number (32-bits). |
| `Float` | Float | Any signed decimal number (32-bits), using floating point arithmatic. |
| `String` | String | Normal text, effectively utf-8. |
| `Array` | Array | A series of values stored sequentially in memory. |
| `Table` | Table | A series key-value pairs stored in a hash table. Booleans, functions, opaques and `null` can't be used as keys. |
| `Function` | Function | A chunk of reusable code that takes zero or more parameters, and may return a result. Functions are declared with the `fn` keyword, or in the API. |
| `Opaque` | Opaque | This value is unusable in Toy, but allows you to pass data between C bindings provided with the API. |
| `Any` | Any | The default type when nothing is specified. It can hold any value. |
## Control Flow
Making a decision, or repeating a chunk of code multiple times, is essential for any language. Choosing between multiple options can be done with the `if-then-else` statement - if the condition is truthy, the 'then-branch' will be executed. Otherwise, the optional 'else-branch' is executed instead.
```
var answer = 42;
if (answer < 56) {
print "Cod dang it!";
}
else {
print "Something's fishy here...";
}
```
```
var challenge = "hard";
if (challenge == "hard") {
print "I choose to build a scripting language, not because it's easy, but because it's hard!";
}
//the else-branch is optional
```
To repeat a certain action, use the `while-then` loop, which repeats the body as long as the given condition remains true on each loop.
```
var loops = 0;
while (loops++ < 8) {
print "These episodes are endless.";
}
```
To break out of a loop, you can use the `break` keyword. Alternatively, to restart the loop early, use the `continue` keyword.
```
var loops = 0;
while (true) {
if (++loops < 15532) {
continue;
}
break; //poor yuki ;_;
}
```
*Note: The `for` loop is coming soon, and will allow for iteration over an array or table, but isn't vital right now.*
## Arrays and Tables
Arrays are defined with a pair of brackets, and can contain a list of comma-separated values.
```
//define an array
var array = [1,2,3];
//specify the type
var bray: Array = [4,5,6];
//define an empty array
var craycray: Array = [];
//arrays are zero-indexed
print array[0]; //'1'
```
Tables are also defined with brackets, and contain a comma-separated list of key-value pairs defined by colons:
```
//most types can be used as keys
var table = ["alpha": 1, "beta": 2, "gamma": 3];
//the 'Table' keyword can define the type, and an empty table still has a colon
var under: Table = [:];
//printing the whole table does NOT guarantee internal order
print table["beta"];
```
## Attributes
Some values, including Strings, Arrays and Tables, have "attributes" which are accessible with the dot `.` operator. These can expose internal values or components for manipulating said values.
```
var string = "Hello World";
print string.length; //11
print string.asUpper; //HELLO WORLD
print string.asLower; //hello world
var array = [1,2,3];
array.pushBack(4); //array = [1,2,3,4]
var element = array.popBack(); //element = 4
var emptyArray = [];
var table = ["alpha": 1, "beta":2];
print table.length; //2
table.insert("key",element); //table["key"] = 4
print table.hasKey("alpha"); //true
table.remove("alpha"); //table = ["beta":2,"key":4]
var emptyTable = [:];
```
Opaques can also be given attributes, but this requires some in-depth understanding of the API, so won't be covered here.
## Functions
Functions are defined with the `fn` keyword, and follow a c-like syntax, with optional types on each parameter:
```toy
fn fib(n: Int) {
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
print fib(12); //144
```
```toy
fn isLeapYear(n: Int) {
if (n % 400 == 0) return true;
if (n % 100 == 0) return false;
return n % 4 == 0;
}
```
## External API and Extending Toy
*Note: Watch this space, docs for the C API are coming soon. For now, the [Cheat Sheet](/cheatsheet) can get you started.*
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

-14
View File
@@ -1,14 +0,0 @@
<!-- open graph protocol -->
<meta property="og:url" content="{{ url }}" />
<meta property="og:type" content="website" />
<meta property="og:image" content="{{ base_url }}/img/toypreview.png" />
<meta property="og:title" content="{{ title }}" />
<meta property="og:description" content="{{ description }}" />
<!-- twitter has to be special -->
<meta name="twitter:card" content="{{ title }}" />
<meta name="twitter:url" content="{{ url}}" />
<meta name="twitter:type" content="website" />
<meta name="twitter:image" content="{{ base_url }}/img/toypreview.png" />
<meta name="twitter:title" content="{{ title }}" />
<meta name="twitter:description" content="{{ description }}" />
+61 -63
View File
@@ -1,83 +1,81 @@
#compiler settings reference
#CC=gcc
#CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
#LIBS+=-lm
#LDFLAGS+=
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
#TODO: release builds should define the NDEBUG flag; double check it works
export TOY_OUTDIR = out
#directories
export TOY_SOURCEDIR=source
export TOY_REPLDIR=repl
export TOY_OUTDIR=out
export TOY_OBJDIR=obj
all: $(TOY_OUTDIR) repl
#targets
all: source repl
#repl builds
repl: $(TOY_OUTDIR) library
$(MAKE) -C repl
.PHONY: source
source:
$(MAKE) -C source -k
repl-static: $(TOY_OUTDIR) static
$(MAKE) -C repl
.PHONY: repl
repl: source
$(MAKE) -C repl -k
repl-release: clean $(TOY_OUTDIR) library-release
$(MAKE) -C repl release
.PHONY: tests tests-ci
tests: clean
$(MAKE) -C tests -k
repl-static-release: clean $(TOY_OUTDIR) static-release
$(MAKE) -C repl release
tests-gdb: clean
$(MAKE) -C tests -k gdb
#lib builds
library: $(TOY_OUTDIR)
$(MAKE) -j8 -C source library
static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static
library-release: $(TOY_OUTDIR)
$(MAKE) -j8 -C source library-release
static-release: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static-release
#utils
test: clean $(TOY_OUTDIR)
$(MAKE) -C test
#util targets
$(TOY_OUTDIR):
mkdir $(TOY_OUTDIR)
$(TOY_OBJDIR):
mkdir $(TOY_OBJDIR)
#utils
install-tools:
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
#util commands
.PHONY: clean
clean:
ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.out' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(shell uname),NetBSD)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.out' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
find . -empty -type d -delete
else ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
rm -rf out
find . -empty -type d -delete
else ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib
$(RM) out
$(RM) obj
$(RM) *.o *.a *.exe
else ifeq ($(shell uname),Darwin)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.out' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.dylib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
rm -rf out
find . -empty -type d -delete
else
@echo "Deletion failed - what platform is this?"
endif
rebuild: clean all
-100
View File
@@ -1,100 +0,0 @@
#include "ast_inspector.h"
#include "toy_console_colors.h"
#include "toy_bucket.h"
#include "toy_string.h"
#include "toy_value.h"
#include <stdio.h>
#include <stdlib.h>
void inspect_by_type(Toy_Ast* ast, int depth);
void inspect_block(Toy_Ast* ast, int depth);
void inspect_value(Toy_Ast* ast, int depth);
void inspect_print(Toy_Ast* ast, int depth);
#define PRINTSTR(x) printf("%*s%s", depth*4, "", x);
static Toy_Bucket* bucket = NULL; //lazy
void inspect_ast(Toy_Ast* ast) {
bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
inspect_by_type(ast, 0);
Toy_freeBucket(&bucket);
}
void inspect_by_type(Toy_Ast* ast, int depth) {
switch(ast->type) {
case TOY_AST_BLOCK:
inspect_block(ast, depth);
return;
case TOY_AST_VALUE:
inspect_value(ast, depth);
return;
// case TOY_AST_UNARY:
// case TOY_AST_BINARY:
// case TOY_AST_BINARY_SHORT_CIRCUIT:
// case TOY_AST_COMPARE:
// case TOY_AST_GROUP:
// case TOY_AST_COMPOUND:
// case TOY_AST_AGGREGATE:
// case TOY_AST_ASSERT:
// case TOY_AST_IF_THEN_ELSE:
// case TOY_AST_WHILE_THEN:
// case TOY_AST_BREAK:
// case TOY_AST_CONTINUE:
// case TOY_AST_RETURN:
case TOY_AST_PRINT:
inspect_print(ast, depth);
return;
// case TOY_AST_VAR_DECLARE:
// case TOY_AST_VAR_ASSIGN:
// case TOY_AST_VAR_ACCESS:
// case TOY_AST_FN_DECLARE:
// case TOY_AST_FN_INVOKE:
// case TOY_AST_STACK_POP:
default:
printf(TOY_CC_WARN "%*sAST %s (unhandled by inspector)" TOY_CC_RESET "\n", depth*4, "", Toy_private_getAstTypeAsCString(ast->type));
}
}
void inspect_block(Toy_Ast* ast, int depth) {
//show the block braces
PRINTSTR("{\n");
if (ast->block.child) {
inspect_by_type(ast->block.child, depth + 1);
if (ast->block.next) {
inspect_block(ast->block.next, depth + 0);
}
}
PRINTSTR("}\n");
}
void inspect_value(Toy_Ast* ast, int depth) {
(void)depth;
Toy_String* str = Toy_stringifyValue(&bucket, ast->value.value);
char* buffer = Toy_getStringRaw(str); //SLOW
printf("%s '%s'", Toy_getValueTypeAsCString(ast->value.value.type), buffer);
free(buffer);
Toy_freeString(str);
}
void inspect_print(Toy_Ast* ast, int depth) {
(void)depth;
PRINTSTR("PRINT ");
inspect_by_type(ast->print.child, depth);
printf(";\n");
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include "toy_ast.h"
void inspect_ast(Toy_Ast* astHandle);
-47
View File
@@ -1,47 +0,0 @@
#include "bucket_inspector.h"
#include <toy_string.h>
#include <stdio.h>
int inspect_bucket(Toy_Bucket** bucketHandle) {
int depth = 0;
//for each bucket
for (Toy_Bucket* iter = (*bucketHandle); iter != NULL; iter = iter->next) {
int occupied = 0;
int released = 0;
unsigned char* ptr = iter->data;
while ((ptr - iter->data < iter->count) && *((int*)ptr) != 0) { //for each partition
if ( ( *((int*)ptr) & 1) == 0) { //is this partition still in use?
occupied++;
//try to print as a string if possible
Toy_String* str = (void*)(ptr + 4);
if (str->info.type == TOY_STRING_LEAF && str->info.length < 255) {
printf("String Leaf (%d bytes, %d refCount): %.*s\n", *((int*)ptr), str->info.refCount, str->info.length, str->leaf.data);
}
else if (str->info.type == TOY_STRING_NODE) {
printf("String Node (%d bytes, %d refCount): ...\n", *((int*)ptr), str->info.refCount);
}
}
else {
released++;
}
//jump distance: ((*((int*)ptr) | 1) ^ 1) + 4
// printf(" jump %d, ", ((*((int*)ptr) | 1) ^ 1) + 4);
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
}
printf("Bucket link %d: count %u, %d occupied, %d released\n", depth, iter->count, occupied, released);
depth++;
}
printf("\n");
return depth;
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include "toy_bucket.h"
int inspect_bucket(Toy_Bucket** bucketHandle);
-387
View File
@@ -1,387 +0,0 @@
#include "bytecode_inspector.h"
#include "toy_console_colors.h"
#include "toy_opcodes.h"
#include "toy_value.h"
#include "toy_string.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr);
int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr);
#define ISPRINT_SANITIZE(x) (isprint((int)x) > 0 ? (x) : '_')
#define MARKER_VALUE(pc, type) \
((unsigned int)(pc * sizeof(type)))
#define MARKER "\t\033[" TOY_CC_FONT_BLACK "m" " %u\t" TOY_CC_RESET
#define FONT_BLACK "\033[" TOY_CC_FONT_BLACK "m"
//exposed functions
int inspect_bytecode(unsigned char* bytecode) {
//TODO: handle version info
unsigned int const bytecodeSize = ((unsigned int*)(bytecode))[0];
unsigned int const jumpsSize = ((unsigned int*)(bytecode))[1];
unsigned int const paramSize = ((unsigned int*)(bytecode))[2];
unsigned int const dataSize = ((unsigned int*)(bytecode))[3];
unsigned int const subsSize = ((unsigned int*)(bytecode))[4];
printf(FONT_BLACK ".header:\r" TOY_CC_RESET);
//bytecode size
printf(MARKER TOY_CC_NOTICE "Bytecode Size: \t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(0, unsigned int), bytecodeSize);
//header counts
printf(MARKER TOY_CC_NOTICE "Jumps Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(1, unsigned int), jumpsSize);
printf(MARKER TOY_CC_NOTICE "Param Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(2, unsigned int), paramSize);
printf(MARKER TOY_CC_NOTICE "Data Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(3, unsigned int), dataSize);
printf(MARKER TOY_CC_NOTICE "Subs Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(4, unsigned int), subsSize);
//some addresses may be absent
unsigned int addr_pc = 4;
unsigned int code_addr = 0;
unsigned int jumps_addr = 0;
unsigned int param_addr = 0;
unsigned int data_addr = 0;
unsigned int subs_addr = 0;
//bugfix
unsigned int code_end = 0;
//header addresses
if (true) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Code Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
code_addr = ((unsigned int*)(bytecode))[addr_pc];
}
if (jumpsSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Jumps Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
jumps_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = jumps_addr;
}
if (paramSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Param Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
param_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = param_addr;
}
if (dataSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Data Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
data_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = data_addr;
}
if (subsSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Subs Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
subs_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = subs_addr;
}
if (code_end == 0) code_end = bytecodeSize; //very hacky
printf(FONT_BLACK ".code:\r" TOY_CC_RESET);
unsigned int pc = code_addr;
while(pc < code_end) {
pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr);
}
//jumps
if (jumpsSize > 0) {
printf(FONT_BLACK ".jumps:\r" TOY_CC_RESET);
for (unsigned int i = 0; (i*4) < jumpsSize; i++) {
printf(MARKER TOY_CC_NOTICE "%u (data %u)" TOY_CC_RESET "\n", MARKER_VALUE(jumps_addr + i, unsigned int),
i,
((unsigned int*)(bytecode + jumps_addr))[i] + data_addr
);
}
}
//param
if (paramSize > 0) {
printf(FONT_BLACK ".param:\r" TOY_CC_RESET);
for (unsigned int i = 0; (i*4) < paramSize; i += 2) {
printf(MARKER TOY_CC_NOTICE "%u (type %s, data %u)" TOY_CC_RESET "\n", MARKER_VALUE(param_addr + i, unsigned int),
i,
Toy_getValueTypeAsCString(((unsigned int*)(bytecode + param_addr))[i + 1]),
((unsigned int*)(bytecode + param_addr))[i] + data_addr
);
}
}
//data; assume there's only strings for now
if (dataSize > 0) {
printf(FONT_BLACK ".data:\r" TOY_CC_RESET);
for (unsigned int i = 0; (i*4) < dataSize; i++) {
printf(MARKER TOY_CC_NOTICE "%c %c %c %c" TOY_CC_RESET "\n", MARKER_VALUE(data_addr + i, unsigned int),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[0]),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[1]),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[2]),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[3])
);
}
}
//subs
if (subsSize > 0) {
printf(FONT_BLACK ".subs:\n" TOY_CC_RESET);
unsigned int i = 0;
while (i < subsSize) {
i += inspect_bytecode(bytecode + subs_addr + i);
}
}
return bytecodeSize;
}
int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr) {
//read and print the opcode instruction at 'pc'
Toy_OpcodeType opcode = bytecode[pc];
switch(opcode) {
case TOY_OPCODE_READ:
return inspect_read(bytecode, pc, jumps_addr, data_addr);
case TOY_OPCODE_DECLARE: {
unsigned int indexValue = *((unsigned int*)(bytecode + pc + 4));
unsigned int jumpValue = *((unsigned int*)(bytecode + jumps_addr + indexValue));
char* cstr = ((char*)(bytecode + data_addr + jumpValue));
printf(MARKER "DECLARE %s: %s%s\n", MARKER_VALUE(pc, unsigned char),
cstr,
Toy_getValueTypeAsCString(bytecode[pc + 1]),
bytecode[pc + 3] ? " const" : ""
);
return 8;
}
case TOY_OPCODE_ASSIGN:
printf(MARKER "ASSIGN %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "(chained)" : "");
return 4;
case TOY_OPCODE_ASSIGN_COMPOUND:
printf(MARKER "ASSIGN COMPOUND %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "(chained)" : "");
return 4;
case TOY_OPCODE_ACCESS:
printf(MARKER "ACCESS\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_INVOKE:
printf(MARKER "INVOKE as '%s' (%d parameters)\n", MARKER_VALUE(pc, unsigned char),
Toy_getValueTypeAsCString(bytecode[pc + 1]),
bytecode[pc + 2]);
return 4;
case TOY_OPCODE_ATTRIBUTE:
printf(MARKER "ATTRIBUTE (accessed from a compound)\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_DUPLICATE:
printf(MARKER "DUPLICATE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "and ACCESS" : "");
return 4;
case TOY_OPCODE_ELIMINATE:
printf(MARKER "ELIMINATE\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_ADD:
printf(MARKER "ADD %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_SUBTRACT:
printf(MARKER "SUBTRACT %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_MULTIPLY:
printf(MARKER "MULTIPLY %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_DIVIDE:
printf(MARKER "DIVIDE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_MODULO:
printf(MARKER "MODULO %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_COMPARE_EQUAL:
printf(MARKER "COMPARE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] != TOY_OPCODE_NEGATE ? "==" : "!=");
return 4;
case TOY_OPCODE_COMPARE_LESS:
printf(MARKER "COMPARE '<'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_COMPARE_LESS_EQUAL:
printf(MARKER "COMPARE '<='\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_COMPARE_GREATER:
printf(MARKER "COMPARE '>'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_COMPARE_GREATER_EQUAL:
printf(MARKER "COMPARE '>='\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_AND:
printf(MARKER "LOGICAL '&&'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_OR:
printf(MARKER "LOGICAL '||'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_TRUTHY:
printf(MARKER "LOGICAL TRUTHY\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_NEGATE:
printf(MARKER "LOGICAL NEGATE\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_RETURN:
printf(MARKER "Keyword RETURN (%u)\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1]);
return 4;
case TOY_OPCODE_JUMP:
printf(MARKER TOY_CC_DEBUG "JUMP %s%s (%s%d) (GOTO %u)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 1] == TOY_OP_PARAM_JUMP_ABSOLUTE ? "absolute" : "relative",
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_ALWAYS ? "" :
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_TRUE ? " if true" : " if false",
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
bytecode[pc + 4],
bytecode[pc + 4] + pc + 8
);
return 8;
case TOY_OPCODE_ESCAPE:
printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
bytecode[pc + 4],
bytecode[pc + 4] + pc + 12,
bytecode[pc + 8]
);
return 12;
case TOY_OPCODE_SCOPE_PUSH:
printf(MARKER "SCOPE PUSH\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_SCOPE_POP:
printf(MARKER "SCOPE POP\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_ASSERT:
printf(MARKER TOY_CC_WARN "Keyword ASSERT (cond%s)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] > 1 ? ",msg" : "");
return 4;
case TOY_OPCODE_PRINT:
printf(MARKER TOY_CC_NOTICE "Keyword PRINT\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_CONCAT:
printf(MARKER "CONCATENATE strings\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_INDEX:
printf(MARKER "INDEX (%d elements)\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1]);
return 4;
// case TOY_OPCODE_UNUSED:
// case TOY_OPCODE_PASS:
// case TOY_OPCODE_ERROR:
// case TOY_OPCODE_EOF:
default:
printf(MARKER TOY_CC_WARN "Unknown Word: [%u, %u, %u, %u]" TOY_CC_RESET "\n", MARKER_VALUE(pc, unsigned char), bytecode[pc], bytecode[pc+1], bytecode[pc+2], bytecode[pc+3]);
return 4;
}
}
int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr) {
Toy_ValueType type = bytecode[pc + 1];
switch(type) {
case TOY_VALUE_NULL: {
printf(MARKER "READ NULL\n", MARKER_VALUE(pc, unsigned char));
return 4;
}
case TOY_VALUE_BOOLEAN: {
if (bytecode[pc + 2]) {
printf(MARKER "READ BOOL true\n", MARKER_VALUE(pc, unsigned char));
}
else {
}
return 4;
}
case TOY_VALUE_INTEGER: {
int i = *(int*)(bytecode + pc + 4);
printf(MARKER "READ INTEGER %d\n", MARKER_VALUE(pc, unsigned char), i);
return 8;
}
case TOY_VALUE_FLOAT: {
float i = *(float*)(bytecode + pc + 4);
printf(MARKER "READ FLOAT %f\n", MARKER_VALUE(pc, unsigned char), i);
return 8;
}
case TOY_VALUE_STRING: {
Toy_StringType stringType = (Toy_StringType)(*(bytecode + pc + 2)); //Probably not needed
int len = bytecode[pc + 3]; //only used for names?
(void)len;
(void)stringType;
unsigned int indexValue = *((unsigned int*)(bytecode + pc + 4));
unsigned int jumpValue = *((unsigned int*)(bytecode + jumps_addr + indexValue));
char* cstr = ((char*)(bytecode + data_addr + jumpValue));
printf(MARKER "READ STRING [%u] '%s'\n", MARKER_VALUE(pc, unsigned char), indexValue, cstr);
return 8;
}
case TOY_VALUE_FUNCTION:
printf(MARKER "READ FUNCTION '%u' (%d params)\n", MARKER_VALUE(pc, unsigned char), *((unsigned int*)(bytecode + pc + 4)), bytecode[pc + 2]);
return 8;
case TOY_VALUE_ARRAY: {
unsigned int count = *((unsigned int*)(bytecode + pc + 4));
printf(MARKER "READ ARRAY %u elements\n", MARKER_VALUE(pc, unsigned char), count);
return 8;
}
case TOY_VALUE_TABLE: {
unsigned int count = *((unsigned int*)(bytecode + pc + 4));
printf(MARKER "READ TABLE %u elements (consuming %u values)\n", MARKER_VALUE(pc, unsigned char), count / 2, count);
return 8;
}
case TOY_VALUE_OPAQUE:
case TOY_VALUE_ANY:
case TOY_VALUE_UNKNOWN:
default: {
printf(MARKER TOY_CC_WARN "READ %s (unhandled by inspector)" TOY_CC_RESET "\n", MARKER_VALUE(pc, unsigned char), Toy_getValueTypeAsCString(type));
return 4;
}
}
}
-3
View File
@@ -1,3 +0,0 @@
#pragma once
int inspect_bytecode(unsigned char* bytecode);
+95
View File
@@ -0,0 +1,95 @@
#include "lib_standard.h"
#include "memory.h"
#include <time.h>
#include <sys/time.h>
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to clock\n");
return -1;
}
//get the time from C (what a pain)
time_t rawtime = time(NULL);
struct tm* timeinfo = localtime( &rawtime );
char* timestr = asctime(timeinfo);
//push to the stack
int len = strlen(timestr) - 1; //-1 for the newline
Literal timeLiteral = TO_STRING_LITERAL(createRefStringLength(timestr, len));
//push to the stack
pushLiteralArray(&interpreter->stack, timeLiteral);
//cleanup
freeLiteral(timeLiteral);
return 1;
}
//call the hook
typedef struct Natives {
char* name;
NativeFn fn;
} Natives;
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
//build the natives list
Natives natives[] = {
{"clock", nativeClock},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
//make sure the name isn't taken
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(alias);
return false;
}
//create the dictionary to load up with functions
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Literal name = TO_STRING_LITERAL(createRefString(natives[i].name));
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
func.type = LITERAL_FUNCTION_NATIVE;
setLiteralDictionary(dictionary, name, func);
freeLiteral(name);
freeLiteral(func);
}
//build the type
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
TYPE_PUSH_SUBTYPE(&type, strType);
TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
declareScopeVariable(interpreter->scope, alias, type);
setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
freeLiteral(dict);
freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "interpreter.h"
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
+412
View File
@@ -0,0 +1,412 @@
#include "lib_timer.h"
#include "memory.h"
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
//normallize
if (x->tv_usec > 999999) {
x->tv_sec += x->tv_usec / 1000000;
x->tv_usec %= 1000000;
}
if (y->tv_usec > 999999) {
y->tv_sec += y->tv_usec / 1000000;
y->tv_usec %= 1000000;
}
//calc
result->tv_sec = x->tv_sec - y->tv_sec;
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
if (result->tv_sec != 0) { //only works far from 0
result->tv_usec += 1000000;
result->tv_sec--; // borrow
}
}
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
}
//god damn it
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
struct timeval* d = ALLOCATE(struct timeval, 1);
//I gave up, copied from SO
timeval_subtract(d, rhs, lhs);
return d;
}
//callbacks
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
gettimeofday(timeinfo, NULL);
//wrap in an opaque literal for Toy
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
pushLiteralArray(&interpreter->stack, timeLiteral);
freeLiteral(timeLiteral);
return 1;
}
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval timerStop;
gettimeofday(&timerStop, NULL);
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(timerStart, &timerStop);
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(diffLiteral);
return 1;
}
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
return -1;
}
//get the args
Literal microsecondLiteral = popLiteralArray(arguments);
Literal secondLiteral = popLiteralArray(arguments);
Literal secondLiteralIdn = secondLiteral;
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
freeLiteral(secondLiteralIdn);
}
Literal microsecondLiteralIdn = microsecondLiteral;
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, &microsecondLiteral)) {
freeLiteral(microsecondLiteralIdn);
}
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
return -1;
}
if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) {
interpreter->errorOutput("Microseconds out of range in createTimer\n");
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
return -1;
}
//get the timeinfo from toy
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
timeinfo->tv_sec = AS_INTEGER(secondLiteral);
timeinfo->tv_usec = AS_INTEGER(microsecondLiteral);
//wrap in an opaque literal for Toy
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
pushLiteralArray(&interpreter->stack, timeLiteral);
freeLiteral(timeLiteral);
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
return 1;
}
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
return -1;
}
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
//create the result literal
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
pushLiteralArray(&interpreter->stack, result);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(result);
return 1;
}
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
return -1;
}
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
//create the result literal
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
pushLiteralArray(&interpreter->stack, result);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(result);
return 1;
}
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
return -1;
}
//unwrap the opaque literals
Literal rhsLiteral = popLiteralArray(arguments);
Literal lhsLiteral = popLiteralArray(arguments);
Literal lhsLiteralIdn = lhsLiteral;
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
freeLiteral(lhsLiteralIdn);
}
Literal rhsLiteralIdn = rhsLiteral;
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
freeLiteral(rhsLiteralIdn);
}
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
freeLiteral(lhsLiteral);
freeLiteral(rhsLiteral);
return -1;
}
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(lhsTimer, rhsTimer);
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
freeLiteral(lhsLiteral);
freeLiteral(rhsLiteral);
freeLiteral(diffLiteral);
return 1;
}
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
return -1;
}
//unwrap in an opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
//create the string literal
Literal resultLiteral = TO_NULL_LITERAL;
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
char buffer[128];
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
resultLiteral = TO_STRING_LITERAL(createRefStringLength(buffer, strlen(buffer)));
}
else { //normal case
char buffer[128];
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
resultLiteral = TO_STRING_LITERAL(createRefStringLength(buffer, strlen(buffer)));
}
pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(resultLiteral);
return 1;
}
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
return -1;
}
//unwrap in an opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
FREE(struct timeval, timer);
freeLiteral(timeLiteral);
return 0;
}
//call the hook
typedef struct Natives {
char* name;
NativeFn fn;
} Natives;
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
//build the natives list
Natives natives[] = {
{"startTimer", nativeStartTimer},
{"_stopTimer", nativeStopTimer},
{"createTimer", nativeCreateTimer},
{"_getTimerSeconds", nativeGetTimerSeconds},
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
{"_compareTimer", nativeCompareTimer},
{"_timerToString", nativeTimerToString},
{"_destroyTimer", nativeDestroyTimer},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
//make sure the name isn't taken
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(alias);
return false;
}
//create the dictionary to load up with functions
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Literal name = TO_STRING_LITERAL(createRefString(natives[i].name));
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
func.type = LITERAL_FUNCTION_NATIVE;
setLiteralDictionary(dictionary, name, func);
freeLiteral(name);
freeLiteral(func);
}
//build the type
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
TYPE_PUSH_SUBTYPE(&type, strType);
TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
declareScopeVariable(interpreter->scope, alias, type);
setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
freeLiteral(dict);
freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "interpreter.h"
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
-532
View File
@@ -1,532 +0,0 @@
#include "ast_inspector.h"
#include "bytecode_inspector.h"
#include "bucket_inspector.h"
#include "toy_console_colors.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_vm.h"
//NOTE: for testing
#include "standard_library.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned char* readFile(char* path, int* size) {
//open the file
FILE* file = fopen(path, "rb");
if (file == NULL) {
*size = -1; //missing file error
return NULL;
}
//determine the file's length
fseek(file, 0L, SEEK_END);
*size = ftell(file);
rewind(file);
//make some space
unsigned char* buffer = malloc(*size + 1);
if (buffer == NULL) {
fclose(file);
return NULL;
}
//read the file
if (fread(buffer, sizeof(unsigned char), *size, file) < (unsigned int)(*size)) {
fclose(file);
free(buffer);
*size = -2; //singal a read error
return NULL;
}
buffer[(*size)++] = '\0';
//clean up and return
fclose(file);
return buffer;
}
int getFileName(char* dest, const char* src, size_t destLength) {
#define MIN(a, b) ((a) < (b) ? (a) : (b))
char* p = NULL;
//find the last slash, regardless of platform
p = strrchr(src, '\\');
if (p == NULL) {
p = strrchr(src, '/');
}
if (p == NULL) {
int len = MIN(strlen(src), destLength-1);
strncpy(dest, src, len);
dest[len] = '\0';
return len;
}
p++; //skip the slash
//determine length of the file name
int len = MIN(strlen(src), destLength-1);
//copy to the dest
strncpy(dest, p, len);
dest[len] = '\0';
return len;
#undef MIN
}
//error callbacks
static int errorAndExitCallback(const char* msg) {
fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
exit(-1);
}
static int errorAndContinueCallback(const char* msg) {
return fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
}
static int assertFailureAndExitCallback(const char* msg) {
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
exit(-1);
}
static int assertFailureAndContinueCallback(const char* msg) {
return fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
}
static int noOpCallback(const char* msg) {
//NO-OP
(void)msg;
return 0;
}
static int silentExitCallback(const char* msg) {
//NO-OP
(void)msg;
exit(-1);
}
//handle command line arguments
typedef struct CmdLine {
bool error;
bool help;
bool version;
char* infile;
int infileLength;
bool silentPrint;
bool silentAssert;
bool removeAssert;
bool verbose;
} CmdLine;
void usageCmdLine(int argc, const char* argv[]) {
(void)argc;
printf("Usage: %s [ -h | -v | -f source.toy ]\n\n", argv[0]);
}
void helpCmdLine(int argc, const char* argv[]) {
usageCmdLine(argc, argv);
printf("The Toy Programming Language, leave arguments blank for an interactive REPL.\n\n");
printf(" -h, --help\t\t\tShow this help then exit.\n");
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
printf(" -f, --file infile\t\tParse, compile and execute the source file then exit.\n");
printf(" --silent-print\t\tSuppress output from the print keyword.\n");
printf(" --silent-assert\t\tSuppress output from the assert keyword.\n");
printf(" --remove-assert\t\tDo not include the assert statement in the bytecode.\n");
printf(" -d, --verbose\t\tPrint debugging information about Toy's internals.\n");
}
void versionCmdLine(int argc, const char* argv[]) {
(void)argc;
(void)argv;
printf("The Toy Programming Language, Version %d.%d.%d %s\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
//copy/pasted from the license file - there's a way to include it directly, but it's too finnicky to bother
const char* license =
"Copyright (c) 2020-2026 Kayne Ruse, KR Game Studios\n"
"\n"
"This software is provided 'as-is', without any express or implied\n"
"warranty. In no event will the authors be held liable for any damages\n"
"arising from the use of this software.\n"
"\n"
"Permission is granted to anyone to use this software for any purpose,\n"
"including commercial applications, and to alter it and redistribute it\n"
"freely, subject to the following restrictions:\n"
"\n"
"1. The origin of this software must not be misrepresented; you must not\n"
"claim that you wrote the original software. If you use this software\n"
"in a product, an acknowledgment in the product documentation would be\n"
"appreciated but is not required.\n"
"2. Altered source versions must be plainly marked as such, and must not be\n"
"misrepresented as being the original software.\n"
"3. This notice may not be removed or altered from any source distribution.\n"
"\n"
;
printf("%s",license);
}
CmdLine parseCmdLine(int argc, const char* argv[]) {
CmdLine cmd = {
.error = false,
.help = false,
.version = false,
.infile = NULL,
.infileLength = 0,
.silentPrint = false,
.silentAssert = false,
.removeAssert = false,
.verbose = false,
};
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
cmd.help = true;
}
else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
cmd.version = true;
}
else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) {
if (argc <= i + 1) {
cmd.error = true;
}
else {
if (cmd.infile != NULL) { //don't leak
free(cmd.infile);
}
i++;
//total space to reserve
cmd.infileLength = strlen(argv[i]) + 1;
cmd.infileLength = (cmd.infileLength + 3) & ~3; //BUGFIX: align to word size
cmd.infile = malloc(cmd.infileLength);
if (cmd.infile == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space while parsing the command line, exiting\n" TOY_CC_RESET);
exit(-1);
}
int len = strlen(argv[i]);
strncpy(cmd.infile, argv[i], len);
cmd.infile[len] = '\0';
}
}
else if (!strcmp(argv[i], "--silent-print")) {
cmd.silentPrint = true;
}
else if (!strcmp(argv[i], "--silent-assert")) {
cmd.silentAssert = true;
}
else if (!strcmp(argv[i], "--remove-assert")) {
cmd.removeAssert = true;
}
else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--verbose")) {
cmd.verbose = true;
}
else {
cmd.error = true;
}
}
return cmd;
}
//debugging
static void debugStackPrint(Toy_Stack* stack) {
//DEBUG: if there's anything on the stack, print it
if (stack->count > 0) {
Toy_Bucket* stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
printf("\n" TOY_CC_NOTICE "Stack Dump" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s" TOY_CC_RESET "\n", "type", "value");
for (unsigned int i = 0; i < stack->count; i++) {
Toy_Value v = ((Toy_Value*)(stack + 1))[i]; //'stack + 1' is a naughty trick
//print type
printf("%-20s", Toy_getValueTypeAsCString(v.type));
//print value
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
char* buffer = Toy_getStringRaw(string);
printf("%-20s", buffer);
free(buffer);
Toy_freeString(string);
printf("\n");
}
Toy_freeBucket(&stringBucket);
}
}
static void debugScopePrint(Toy_Scope* scope, int depth) {
//DEBUG: if there's anything in the scope, print it
if (scope->count > 0) {
Toy_Bucket* stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
printf("\n" TOY_CC_NOTICE "Scope Dump [%d]" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s%-20s" TOY_CC_RESET "\n", depth, "type", "name", "value");
for (unsigned int i = 0; i < scope->capacity; i++) {
if (scope->data[i].key == NULL || scope->data[i].key->info.length == 0) {
continue;
}
Toy_String* k = scope->data[i].key;
Toy_Value v = scope->data[i].value;
printf("%-10s%-10s%-20s", Toy_getValueTypeAsCString(scope->data[i].type), scope->data[i].constant ? "const" : "", k != NULL ? k->leaf.data : "");
//print value
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
char* buffer = Toy_getStringRaw(string);
printf("%-20s", buffer);
free(buffer);
Toy_freeString(string);
printf("\n");
}
Toy_freeBucket(&stringBucket);
}
if (scope->next != NULL) {
debugScopePrint(scope->next, depth + 1);
}
}
//repl function
int repl(const char* filepath, bool verbose) {
//output options
Toy_setPrintCallback(puts);
Toy_setErrorCallback(errorAndContinueCallback);
Toy_setAssertFailureCallback(assertFailureAndContinueCallback);
//vars to use
char prompt[256];
getFileName(prompt, filepath, 256);
unsigned int INPUT_BUFFER_SIZE = 4096;
char inputBuffer[INPUT_BUFFER_SIZE];
memset(inputBuffer, 0, INPUT_BUFFER_SIZE);
Toy_VM vm;
Toy_initVM(&vm);
printf("%s> ", prompt); //shows the terminal prompt and begin
//read from the terminal
while(fgets(inputBuffer, INPUT_BUFFER_SIZE, stdin)) {
//work around fgets() adding a newline
unsigned int length = strlen(inputBuffer);
if (inputBuffer[length - 1] == '\n') {
inputBuffer[--length] = '\0';
}
if (length == 0 || !inputBuffer[ strspn(inputBuffer, " \r\n\t") ]) {
printf("%s> ", prompt); //shows the terminal prompt and restart
continue;
}
//end
if (strlen(inputBuffer) == 4 && (strncmp(inputBuffer, "exit", 4) == 0 || strncmp(inputBuffer, "quit", 4) == 0)) {
break;
}
//parse the input, prep the VM for execution
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Lexer lexer;
Toy_bindLexer(&lexer, inputBuffer);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
//parsing error, retry
if (parser.error || ast == NULL) {
Toy_freeBucket(&bucket);
printf("%s> ", prompt); //shows the terminal prompt
continue;
}
if (verbose) {
inspect_ast(ast);
}
unsigned char* bytecode = Toy_compileToBytecode(ast);
Toy_freeBucket(&bucket); //no need to for the GC here
if (bytecode == NULL) {
printf("%s> ", prompt);
continue;
}
if (verbose) {
inspect_bytecode(bytecode);
}
//WARN: Hacky debugging
if (vm.scope == NULL) {
Toy_bindVM(&vm, bytecode, NULL);
initStandardLibrary(&vm);
}
else {
Toy_bindVM(&vm, bytecode, NULL);
}
//run
Toy_runVM(&vm);
int depthBeforeGC = 0;
int depthAfterGC = 0;
//print the debug info
if (verbose) {
debugStackPrint(vm.stack);
debugScopePrint(vm.scope, 0);
depthBeforeGC = inspect_bucket(&vm.memoryBucket);
}
//free the memory, and leave the VM ready for the next loop
Toy_resetVM(&vm, true, true);
if (verbose) {
depthAfterGC = inspect_bucket(&vm.memoryBucket);
printf("GC Report: %d -> %d\n", depthBeforeGC, depthAfterGC);
}
free(bytecode);
printf("%s> ", prompt); //shows the terminal prompt
}
//cleanup all memory
Toy_freeVM(&vm);
return 0;
}
//main file
int main(int argc, const char* argv[]) {
Toy_setPrintCallback(puts);
Toy_setErrorCallback(errorAndExitCallback);
Toy_setAssertFailureCallback(assertFailureAndExitCallback);
//if there's args, process them
CmdLine cmd = parseCmdLine(argc, argv);
//output options
if (cmd.silentPrint) {
Toy_setPrintCallback(noOpCallback);
}
if (cmd.silentAssert) {
Toy_setAssertFailureCallback(silentExitCallback);
}
//process
if (cmd.error) {
usageCmdLine(argc, argv);
}
else if (cmd.help) {
helpCmdLine(argc, argv);
}
else if (cmd.version) {
versionCmdLine(argc, argv);
}
else if (cmd.infile != NULL) {
//read the source file
int size;
unsigned char* source = readFile(cmd.infile, &size);
//check the file
if (source == NULL) {
if (size == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Could not parse an empty file '%s', exiting\n" TOY_CC_RESET, cmd.infile);
return -1;
}
else if (size == -1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: File not found '%s', exiting\n" TOY_CC_RESET, cmd.infile);
return -1;
}
else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown error while reading file '%s', exiting\n" TOY_CC_RESET, cmd.infile);
return -1;
}
}
free(cmd.infile);
cmd.infile = NULL;
cmd.infileLength = 0;
//compile the source code
Toy_Lexer lexer;
Toy_bindLexer(&lexer, (char*)source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
if (ast == NULL) {
Toy_freeBucket(&bucket);
free(source);
return -1;
}
if (cmd.verbose) {
inspect_ast(ast);
}
unsigned char* bytecode = Toy_compileToBytecode(ast);
Toy_freeBucket(&bucket);
free(source);
if (bytecode == NULL) {
return -1;
}
if (cmd.verbose) {
inspect_bytecode(bytecode);
}
//run the compiled code
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, bytecode, NULL);
initStandardLibrary(&vm); //WARN: Hacky debugging
Toy_runVM(&vm);
//print the debug info
if (cmd.verbose) {
debugStackPrint(vm.stack);
debugScopePrint(vm.scope, 0);
}
//cleanup
Toy_freeVM(&vm);
free(bytecode);
}
else {
repl(argv[0], cmd.verbose);
}
return 0;
}
+24 -51
View File
@@ -1,63 +1,36 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm -lToy
LDFLAGS+=-Wl,-rpath,'$$ORIGIN'
ifeq ($(shell uname),Darwin) #make sure there's enough space for the dylib fix
LDFLAGS+=-Wl,-headerpad_max_install_names
endif
IDIR+=. ../source
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=-ltoy
ODIR = obj
SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUTNAME=toy
OUT=../$(TOY_OUTDIR)/toyrepl
#directories
REPL_ROOTDIR=..
REPL_REPLDIR=.
REPL_SOURCEDIR=$(REPL_ROOTDIR)/$(TOY_SOURCEDIR)
REPL_OUTDIR=$(REPL_ROOTDIR)/$(TOY_OUTDIR)
REPL_OBJDIR=$(TOY_OBJDIR)
#file names
REPL_REPLFILES=$(wildcard $(REPL_REPLDIR)/*.c)
REPL_OBJFILES=$(addprefix $(REPL_OBJDIR)/,$(notdir $(REPL_REPLFILES:.c=.o)))
REPL_TARGETNAME=repl
#file extensions
ifeq ($(OS),Windows_NT)
REPL_TARGETEXT=.exe
all: $(OBJ)
ifeq ($(shell uname),Darwin)
cp $(PWD)/$(TOY_OUTDIR)/lib$(OUTNAME).dylib /usr/local/lib/
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) $(LIBS)
else
REPL_TARGETEXT=.out
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -Wl,-rpath,. -L$(realpath $(shell pwd)/../$(TOY_OUTDIR)) $(LIBS)
endif
#linker fix
LDFLAGS+=-L$(realpath $(REPL_OUTDIR))
release: all
strip $(OUT)
#build the object files, compile the test cases, and run
all: build link
$(OBJ): | $(ODIR)
#targets for each step
.PHONY: build
build: $(REPL_OBJDIR) $(REPL_OBJFILES)
$(ODIR):
mkdir $(ODIR)
.PHONY: link
link: $(REPL_OUTDIR) $(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
#util targets
$(REPL_OUTDIR):
mkdir $(REPL_OUTDIR)
.PHONY: clean
$(REPL_OBJDIR):
mkdir $(REPL_OBJDIR)
#compilation steps
$(REPL_OBJDIR)/%.o: $(REPL_REPLDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(REPL_REPLDIR)) $(addprefix -I,$(REPL_SOURCEDIR)) $(CFLAGS)
$(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT): $(REPL_OBJFILES)
$(CC) -DTOY_IMPORT $(CFLAGS) -o $@ $(REPL_OBJFILES) $(LDFLAGS) $(LIBS)
ifeq ($(shell uname),Darwin) #dylib fix
otool -L $@
install_name_tool -add_rpath @executable_path/. $@
install_name_tool -change $(REPL_OUTDIR)/libToy.dylib @executable_path/libToy.dylib $@
otool -L $@
endif
clean:
$(RM) $(ODIR)
rm /usr/local/lib/lib$(OUTNAME).dylib
+141
View File
@@ -0,0 +1,141 @@
#include "repl_tools.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "console_colors.h"
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void repl() {
//repl does it's own thing for now
bool error = false;
const int size = 2048;
char input[size];
memset(input, 0, size);
Interpreter interpreter; //persist the interpreter for the scopes
initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
for(;;) {
printf("> ");
fgets(input, size, stdin);
//escape the repl (length of 5 to accomodate the newline)
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
break;
}
//setup this iteration
Lexer lexer;
Parser parser;
Compiler compiler;
initLexer(&lexer, input);
initParser(&parser, &lexer);
initCompiler(&compiler);
//run this iteration
ASTNode* node = scanParser(&parser);
while(node != NULL) {
//pack up and restart
if (node->type == AST_NODE_ERROR) {
printf(ERROR "error node detected\n" RESET);
error = true;
freeASTNode(node);
break;
}
writeCompiler(&compiler, node);
freeASTNode(node);
node = scanParser(&parser);
}
if (!error) {
//get the bytecode dump
int size = 0;
unsigned char* tb = collateCompiler(&compiler, &size);
//run the bytecode
runInterpreter(&interpreter, tb, size);
}
//clean up this iteration
freeCompiler(&compiler);
freeParser(&parser);
error = false;
}
freeInterpreter(&interpreter);
}
//entry point
int main(int argc, const char* argv[]) {
initCommand(argc, argv);
//command specific actions
if (command.error) {
usageCommand(argc, argv);
return 0;
}
if (command.help) {
helpCommand(argc, argv);
return 0;
}
if (command.version) {
copyrightCommand(argc, argv);
return 0;
}
//version
if (command.verbose) {
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
}
//run source file
if (command.sourcefile) {
runSourceFile(command.sourcefile);
return 0;
}
//run from stdin
if (command.source) {
runSource(command.source);
return 0;
}
//compile source file
if (command.compilefile && command.outfile) {
size_t size = 0;
char* source = readFile(command.compilefile, &size);
unsigned char* tb = compileString(source, &size);
if (!tb) {
return 1;
}
writeFile(command.outfile, tb, size);
return 0;
}
//run binary
if (command.binaryfile) {
runBinaryFile(command.binaryfile);
return 0;
}
repl();
return 0;
}
+143
View File
@@ -0,0 +1,143 @@
#include "repl_tools.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "console_colors.h"
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include <stdio.h>
#include <stdlib.h>
//IO functions
char* readFile(char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
if (file == NULL) {
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
exit(-1);
}
fseek(file, 0L, SEEK_END);
*fileSize = ftell(file);
rewind(file);
char* buffer = (char*)malloc(*fileSize + 1);
if (buffer == NULL) {
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
exit(-1);
}
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
if (bytesRead < *fileSize) {
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
exit(-1);
}
fclose(file);
return buffer;
}
void writeFile(char* path, unsigned char* bytes, size_t size) {
FILE* file = fopen(path, "wb");
if (file == NULL) {
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
exit(-1);
}
int written = fwrite(bytes, size, 1, file);
if (written != 1) {
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
exit(-1);
}
fclose(file);
}
//repl functions
unsigned char* compileString(char* source, size_t* size) {
Lexer lexer;
Parser parser;
Compiler compiler;
initLexer(&lexer, source);
initParser(&parser, &lexer);
initCompiler(&compiler);
//run the parser until the end of the source
ASTNode* node = scanParser(&parser);
while(node != NULL) {
//pack up and leave
if (node->type == AST_NODE_ERROR) {
printf(ERROR "error node detected\n" RESET);
freeASTNode(node);
freeCompiler(&compiler);
freeParser(&parser);
return NULL;
}
writeCompiler(&compiler, node);
freeASTNode(node);
node = scanParser(&parser);
}
//get the bytecode dump
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
//cleanup
freeCompiler(&compiler);
freeParser(&parser);
//no lexer to clean up
//finally
return tb;
}
void runBinary(unsigned char* tb, size_t size) {
Interpreter interpreter;
initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter);
}
void runBinaryFile(char* fname) {
size_t size = 0; //not used
unsigned char* tb = (unsigned char*)readFile(fname, &size);
if (!tb) {
return;
}
runBinary(tb, size);
//interpreter takes ownership of the binary data
}
void runSource(char* source) {
size_t size = 0;
unsigned char* tb = compileString(source, &size);
if (!tb) {
return;
}
runBinary(tb, size);
}
void runSourceFile(char* fname) {
size_t size = 0; //not used
char* source = readFile(fname, &size);
runSource(source);
free((void*)source);
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "toy_common.h"
char* readFile(char* path, size_t* fileSize);
void writeFile(char* path, unsigned char* bytes, size_t size);
unsigned char* compileString(char* source, size_t* size);
void runBinary(unsigned char* tb, size_t size);
void runBinaryFile(char* fname);
void runSource(char* source);
void runSourceFile(char* fname);
-106
View File
@@ -1,106 +0,0 @@
#include "standard_library.h"
#include "toy_console_colors.h"
#include "toy_print.h"
#include "toy_scope.h"
#include "toy_stack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct CallbackPairs {
const char* name;
Toy_nativeCallback callback;
} CallbackPairs;
//example callbacks
static void answer(Toy_VM* vm, Toy_FunctionNative* self) {
(void)vm;
(void)self;
Toy_print(TOY_CC_DEBUG "This function returns the integer '42' to the calling scope." TOY_CC_RESET);
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(42));
}
static void identity(Toy_VM* vm, Toy_FunctionNative* self) {
//does nothing, but any arguements are left on the stack as results
(void)vm;
(void)self;
}
static void echo(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
//pops one argument, and prints it
Toy_Value value = Toy_popStack(&vm->stack);
Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value);
char* cstr = Toy_getStringRaw(string);
Toy_print(cstr);
free(cstr);
Toy_freeString(string);
Toy_freeValue(value);
}
static void next(Toy_VM* vm, Toy_FunctionNative* self) {
//used by 'range'
if (self->meta2 < self->meta1) {
Toy_Value result = TOY_VALUE_FROM_INTEGER(self->meta2);
Toy_pushStack(&vm->stack, result);
self->meta2++;
}
else {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
}
}
static void range(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
//one arg to represent the number of iterations
Toy_Value value = Toy_popStack(&vm->stack);
//check types
if (!TOY_VALUE_IS_INTEGER(value)) {
char buffer[256];
snprintf(buffer, 256, "Expected Integer argument in 'range', found '%s'", Toy_getValueTypeAsCString(value.type));
Toy_error(buffer);
Toy_freeValue(value);
return;
}
//make the callback
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, next);
fn->native.meta1 = TOY_VALUE_AS_INTEGER(value); //fake a closure
fn->native.meta2 = 0; //counter
Toy_Value result = TOY_VALUE_FROM_FUNCTION(fn);
Toy_pushStack(&vm->stack, result);
}
CallbackPairs callbackPairs[] = {
{"dbg_answer", answer},
{"dbg_identity", identity},
{"dbg_echo", echo},
{"range", range},
{NULL, NULL},
};
//exposed functions
void initStandardLibrary(Toy_VM* vm) {
if (vm == NULL || vm->scope == NULL || vm->memoryBucket == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't initialize standard library, exiting\n" TOY_CC_RESET);
exit(-1);
}
//declare each pair
for (int i = 0; callbackPairs[i].name; i++) {
Toy_String* key = Toy_createStringLength(&vm->memoryBucket, callbackPairs[i].name, strlen(callbackPairs[i].name));
Toy_Function* fn = Toy_createFunctionFromCallback(&(vm->memoryBucket), callbackPairs[i].callback);
Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true);
}
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include "toy_vm.h"
void initStandardLibrary(Toy_VM*);
-16
View File
@@ -1,16 +0,0 @@
//calculate the nth fibonacci number, and print it
var counter: Int = 0;
var first: Int = 1;
var second: Int = 0;
while (counter < 100_000) {
var third: Int = first + second;
first = second;
second = third;
print third;
++counter;
}
-11
View File
@@ -1,11 +0,0 @@
fn output(arg) {
print arg;
}
var array = ["alpha", "bravo", "charlie"];
array.forEach(echo);
array.forEach(output);
+89
View File
@@ -0,0 +1,89 @@
//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";
+9 -10
View File
@@ -1,13 +1,12 @@
//tentatively functional
fn fib(n : int) {
if (n < 2) {
return n;
}
//fibonacci sequence
fn fib(n) {
if (n < 2) return n;
return fib(n-1) + fib(n-2);
return fib(n-1) + fib(n-2);
}
print fib(12);
//Note to my future self: yes, the base case in 'fib()' is 'n < 2', stop second guessing yourself!
//Note to my past self: don't tell me what to do!
//Note to both of you: keep it down you young whipper snappers!
for (var i = 0; i < 20; i++) {
var res = fib(i);
print string i + ": " + string res;
}
-24
View File
@@ -1,24 +0,0 @@
//standard example, using 'while' instead of 'for', because it's not ready yet
var counter: Int = 0;
while (++counter <= 100) {
var result: String = "";
if (counter % 3 == 0) {
result = result .. "fizz";
}
if (counter % 5 == 0) {
result = result .. "buzz";
}
//finally
if (result != "") {
print result;
}
else {
print counter;
}
}
-35
View File
@@ -1,35 +0,0 @@
//WARN: This is just a scratch pad, don't use it
//TODO: table.hasValue or table.getKeyFromValue?
//for (var i in array) print i;
//for (var i in table) print i;
//for (var i in range(10)) print i;
//for (range(10)) print "ha";
//example of a `range`-like function
fn range(limit: Int) {
var counter: Int = 0;
fn next() {
if (counter >= limit) {
return null;
}
else return counter++;
}
return next;
}
var next = range(10);
fn log(x) {
if (x == null) return;
print x;
}
while (true) {
log(next());
}
-12
View File
@@ -1,12 +0,0 @@
fn a(x) {
print x;
}
fn b() {
return 42;
}
a(b(), b());
-19
View File
@@ -1,19 +0,0 @@
//find the leap years
fn isLeapYear(n: Int) {
if (n % 400 == 0) return true;
if (n % 100 == 0) return false;
return n % 4 == 0;
}
//check for string reuse
{
print isLeapYear(1999);
}
{
print isLeapYear(2000);
}
{
print isLeapYear(2004);
}
-12
View File
@@ -1,12 +0,0 @@
var randi: Int = 69420;
fn rand() {
return randi = randi * 1664525 + 1013904223;
}
var a = rand();
+49
View File
@@ -0,0 +1,49 @@
//number of iterations
var SIZE: int const = 100;
//lookup table
var lookup = [
"*": [
"*": [
"*": " ",
" ": "*"
],
" ": [
"*": "*",
" ": " "
]
], " ": [
"*": [
"*": "*",
" ": "*"
],
" ": [
"*": "*",
" ": " "
]
]];
//initial line to build from
var prev: string = "";
for (var i = 0; i < SIZE -1; i++) {
prev += " ";
}
prev += "*"; //initial
print prev;
//run
for (var iteration = 0; iteration < 100; iteration++) {
//left
var output = (lookup[" "][prev[0]][prev[1]]);
//middle
for (var i = 1; i < SIZE-1; i++) {
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
}
//right
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
print output;
prev = output;
}
+10
View File
@@ -0,0 +1,10 @@
{
var t = astype [int];
var arr: t = [1, 2, 3.14];
}
{
var t = astype [string:int];
var dict: t = ["one": 1, "two": 2, "pi": 3.14];
}
-21
View File
@@ -1,21 +0,0 @@
fn makeCounter() {
var counter: Int = 0;
fn increment() {
return ++counter;
}
return increment;
}
var tally = makeCounter();
while (true) {
var result = tally();
print result;
if (result >= 10) {
break;
}
}
+368
View File
@@ -0,0 +1,368 @@
#include "ast_node.h"
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>
void freeASTNodeCustom(ASTNode* node, bool freeSelf) {
//don't free a NULL node
if (node == NULL) {
return;
}
switch(node->type) {
case AST_NODE_ERROR:
//NO-OP
break;
case AST_NODE_LITERAL:
freeLiteral(node->atomic.literal);
break;
case AST_NODE_UNARY:
freeASTNode(node->unary.child);
break;
case AST_NODE_BINARY:
freeASTNode(node->binary.left);
freeASTNode(node->binary.right);
break;
case AST_NODE_GROUPING:
freeASTNode(node->grouping.child);
break;
case AST_NODE_BLOCK:
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
break;
case AST_NODE_COMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
break;
case AST_NODE_PAIR:
freeASTNode(node->pair.left);
freeASTNode(node->pair.right);
break;
case AST_NODE_INDEX:
freeASTNode(node->index.first);
freeASTNode(node->index.second);
freeASTNode(node->index.third);
break;
case AST_NODE_VAR_DECL:
freeLiteral(node->varDecl.identifier);
freeLiteral(node->varDecl.typeLiteral);
freeASTNode(node->varDecl.expression);
break;
case AST_NODE_FN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case AST_NODE_FN_DECL:
freeLiteral(node->fnDecl.identifier);
freeASTNode(node->fnDecl.arguments);
freeASTNode(node->fnDecl.returns);
freeASTNode(node->fnDecl.block);
break;
case AST_NODE_FN_CALL:
freeASTNode(node->fnCall.arguments);
break;
case AST_NODE_FN_RETURN:
freeASTNode(node->returns.returns);
break;
case AST_NODE_IF:
freeASTNode(node->pathIf.condition);
freeASTNode(node->pathIf.thenPath);
freeASTNode(node->pathIf.elsePath);
break;
case AST_NODE_WHILE:
freeASTNode(node->pathWhile.condition);
freeASTNode(node->pathWhile.thenPath);
break;
case AST_NODE_FOR:
freeASTNode(node->pathFor.preClause);
freeASTNode(node->pathFor.postClause);
freeASTNode(node->pathFor.condition);
freeASTNode(node->pathFor.thenPath);
break;
case AST_NODE_BREAK:
//NO-OP
break;
case AST_NODE_CONTINUE:
//NO-OP
break;
case AST_NODE_PREFIX_INCREMENT:
freeLiteral(node->prefixIncrement.identifier);
break;
case AST_NODE_PREFIX_DECREMENT:
freeLiteral(node->prefixDecrement.identifier);
break;
case AST_NODE_POSTFIX_INCREMENT:
freeLiteral(node->postfixIncrement.identifier);
break;
case AST_NODE_POSTFIX_DECREMENT:
freeLiteral(node->postfixDecrement.identifier);
break;
case AST_NODE_IMPORT:
freeLiteral(node->import.identifier);
freeLiteral(node->import.alias);
break;
}
if (freeSelf) {
FREE(ASTNode, node);
}
}
void freeASTNode(ASTNode* node) {
freeASTNodeCustom(node, true);
}
//various emitters
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODE_LITERAL;
(*nodeHandle)->atomic.literal = copyLiteral(literal);
}
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODE_UNARY;
(*nodeHandle)->unary.opcode = opcode;
(*nodeHandle)->unary.child = child;
}
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_BINARY;
tmp->binary.opcode = opcode;
tmp->binary.left = *nodeHandle;
tmp->binary.right = rhs;
*nodeHandle = tmp;
}
void emitASTNodeGrouping(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_GROUPING;
tmp->grouping.child = *nodeHandle;
*nodeHandle = tmp;
}
void emitASTNodeBlock(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_BLOCK;
tmp->block.nodes = NULL; //NOTE: appended by the parser
tmp->block.capacity = 0;
tmp->block.count = 0;
*nodeHandle = tmp;
}
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_COMPOUND;
tmp->compound.literalType = literalType;
tmp->compound.nodes = NULL;
tmp->compound.capacity = 0;
tmp->compound.count = 0;
*nodeHandle = tmp;
}
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right) {
//set - assume the node has already been allocated
node->type = AST_NODE_PAIR;
node->pair.left = left;
node->pair.right = right;
}
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_INDEX;
tmp->index.first = first;
tmp->index.second = second;
tmp->index.third = third;
*nodeHandle = tmp;
}
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_VAR_DECL;
tmp->varDecl.identifier = identifier;
tmp->varDecl.typeLiteral = typeLiteral;
tmp->varDecl.expression = expression;
*nodeHandle = tmp;
}
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_FN_COLLECTION;
tmp->fnCollection.nodes = NULL;
tmp->fnCollection.capacity = 0;
tmp->fnCollection.count = 0;
*nodeHandle = tmp;
}
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_FN_DECL;
tmp->fnDecl.identifier = identifier;
tmp->fnDecl.arguments = arguments;
tmp->fnDecl.returns = returns;
tmp->fnDecl.block = block;
*nodeHandle = tmp;
}
void emitASTNodeFnCall(ASTNode** nodeHandle, ASTNode* arguments) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_FN_CALL;
tmp->fnCall.arguments = arguments;
tmp->fnCall.argumentCount = arguments->fnCollection.count;
*nodeHandle = tmp;
}
void emitASTNodeFnReturn(ASTNode** nodeHandle, ASTNode* returns) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_FN_RETURN;
tmp->returns.returns = returns;
*nodeHandle = tmp;
}
void emitASTNodeIf(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_IF;
tmp->pathIf.condition = condition;
tmp->pathIf.thenPath = thenPath;
tmp->pathIf.elsePath = elsePath;
*nodeHandle = tmp;
}
void emitASTNodeWhile(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_WHILE;
tmp->pathWhile.condition = condition;
tmp->pathWhile.thenPath = thenPath;
*nodeHandle = tmp;
}
void emitASTNodeFor(ASTNode** nodeHandle, ASTNode* preClause, ASTNode* condition, ASTNode* postClause, ASTNode* thenPath) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_FOR;
tmp->pathFor.preClause = preClause;
tmp->pathFor.condition = condition;
tmp->pathFor.postClause = postClause;
tmp->pathFor.thenPath = thenPath;
*nodeHandle = tmp;
}
void emitASTNodeBreak(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_BREAK;
*nodeHandle = tmp;
}
void emitASTNodeContinue(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_CONTINUE;
*nodeHandle = tmp;
}
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_PREFIX_INCREMENT;
tmp->prefixIncrement.identifier = copyLiteral(identifier);
*nodeHandle = tmp;
}
void emitASTNodePrefixDecrement(ASTNode** nodeHandle, Literal identifier) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_PREFIX_DECREMENT;
tmp->prefixDecrement.identifier = copyLiteral(identifier);
*nodeHandle = tmp;
}
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_POSTFIX_INCREMENT;
tmp->postfixIncrement.identifier = copyLiteral(identifier);
*nodeHandle = tmp;
}
void emitASTNodePostfixDecrement(ASTNode** nodeHandle, Literal identifier) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_POSTFIX_DECREMENT;
tmp->postfixDecrement.identifier = copyLiteral(identifier);
*nodeHandle = tmp;
}
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODE_IMPORT;
tmp->import.identifier = copyLiteral(identifier);
tmp->import.alias = copyLiteral(alias);
*nodeHandle = tmp;
}
+257
View File
@@ -0,0 +1,257 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
#include "opcodes.h"
#include "token_types.h"
//nodes are the intermediaries between parsers and compilers
typedef union _node ASTNode;
typedef enum ASTNodeType {
AST_NODE_ERROR,
AST_NODE_LITERAL, //a simple value
AST_NODE_UNARY, //one child + opcode
AST_NODE_BINARY, //two children, left and right + opcode
AST_NODE_GROUPING, //one child
AST_NODE_BLOCK, //contains a sub-node array
AST_NODE_COMPOUND, //contains a sub-node array
AST_NODE_PAIR, //contains a left and right
AST_NODE_INDEX, //index a variable
AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
AST_NODE_FN_COLLECTION, //parts of a function
AST_NODE_FN_CALL, //call a function
AST_NODE_FN_RETURN, //for control flow
AST_NODE_IF, //for control flow
AST_NODE_WHILE, //for control flow
AST_NODE_FOR, //for control flow
AST_NODE_BREAK, //for control flow
AST_NODE_CONTINUE, //for control flow
AST_NODE_PREFIX_INCREMENT, //increment a variable
AST_NODE_POSTFIX_INCREMENT, //increment a variable
AST_NODE_PREFIX_DECREMENT, //decrement a variable
AST_NODE_POSTFIX_DECREMENT, //decrement a variable
AST_NODE_IMPORT, //import a library
} ASTNodeType;
//literals
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
typedef struct NodeLiteral {
ASTNodeType type;
Literal literal;
} NodeLiteral;
//unary operator
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
typedef struct NodeUnary {
ASTNodeType type;
Opcode opcode;
ASTNode* child;
} NodeUnary;
//binary operator
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
typedef struct NodeBinary {
ASTNodeType type;
Opcode opcode;
ASTNode* left;
ASTNode* right;
} NodeBinary;
//grouping of other AST nodes
void emitASTNodeGrouping(ASTNode** nodeHandle);
typedef struct NodeGrouping {
ASTNodeType type;
ASTNode* child;
} NodeGrouping;
//block of statement nodes
void emitASTNodeBlock(ASTNode** nodeHandle);
typedef struct NodeBlock {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeBlock;
//compound literals (array, dictionary)
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
typedef struct NodeCompound {
ASTNodeType type;
LiteralType literalType;
ASTNode* nodes;
int capacity;
int count;
} NodeCompound;
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right); //NOTE: this is a set function, not an emit function
typedef struct NodePair {
ASTNodeType type;
ASTNode* left;
ASTNode* right;
} NodePair;
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
typedef struct NodeIndex {
ASTNodeType type;
ASTNode* first;
ASTNode* second;
ASTNode* third;
} NodeIndex;
//variable declaration
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
typedef struct NodeVarDecl {
ASTNodeType type;
Literal identifier;
Literal typeLiteral;
ASTNode* expression;
} NodeVarDecl;
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
void emitASTNodeFnCollection(ASTNode** nodeHandle);
typedef struct NodeFnCollection {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeFnCollection;
//function declaration
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
typedef struct NodeFnDecl {
ASTNodeType type;
Literal identifier;
ASTNode* arguments;
ASTNode* returns;
ASTNode* block;
} NodeFnDecl;
//function call
void emitASTNodeFnCall(ASTNode** nodeHandle, ASTNode* arguments);
typedef struct NodeFnCall {
ASTNodeType type;
ASTNode* arguments;
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
} NodeFnCall;
//function return
void emitASTNodeFnReturn(ASTNode** nodeHandle, ASTNode* returns);
typedef struct NodeFnReturn {
ASTNodeType type;
ASTNode* returns;
} NodeFnReturn;
//control flow path - if-else, while, for, break, continue, return
void emitASTNodeIf(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
void emitASTNodeWhile(ASTNode** nodeHandle, ASTNode* condition, ASTNode* thenPath);
void emitASTNodeFor(ASTNode** nodeHandle, ASTNode* preClause, ASTNode* condition, ASTNode* postClause, ASTNode* thenPath);
void emitASTNodeBreak(ASTNode** nodeHandle);
void emitASTNodeContinue(ASTNode** nodeHandle);
typedef struct NodeIf {
ASTNodeType type;
ASTNode* condition;
ASTNode* thenPath;
ASTNode* elsePath;
} NodeIf;
typedef struct NodeWhile {
ASTNodeType type;
ASTNode* condition;
ASTNode* thenPath;
} NodeWhile;
typedef struct NodeFor {
ASTNodeType type;
ASTNode* preClause;
ASTNode* condition;
ASTNode* postClause;
ASTNode* thenPath;
} NodeFor;
typedef struct NodeBreak {
ASTNodeType type;
} NodeBreak;
typedef struct NodeContinue {
ASTNodeType type;
} NodeContinue;
//pre-post increment/decrement
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier);
void emitASTNodePrefixDecrement(ASTNode** nodeHandle, Literal identifier);
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier);
void emitASTNodePostfixDecrement(ASTNode** nodeHandle, Literal identifier);
typedef struct NodePrefixIncrement {
ASTNodeType type;
Literal identifier;
} NodePrefixIncrement;
typedef struct NodePrefixDecrement {
ASTNodeType type;
Literal identifier;
} NodePrefixDecrement;
typedef struct NodePostfixIncrement {
ASTNodeType type;
Literal identifier;
} NodePostfixIncrement;
typedef struct NodePostfixDecrement {
ASTNodeType type;
Literal identifier;
} NodePostfixDecrement;
//import a library
void emitASTNodeImport(ASTNode** nodeHandle, Literal identifier, Literal alias);
typedef struct NodeImport {
ASTNodeType type;
Literal identifier;
Literal alias;
} NodeImport;
union _node {
ASTNodeType type;
NodeLiteral atomic;
NodeUnary unary;
NodeBinary binary;
NodeGrouping grouping;
NodeBlock block;
NodeCompound compound;
NodePair pair;
NodeIndex index;
NodeVarDecl varDecl;
NodeFnCollection fnCollection;
NodeFnDecl fnDecl;
NodeFnCall fnCall;
NodeFnReturn returns;
NodeIf pathIf;
NodeWhile pathWhile;
NodeFor pathFor;
NodeBreak pathBreak;
NodeContinue pathContinue;
NodePrefixIncrement prefixIncrement;
NodePrefixDecrement prefixDecrement;
NodePostfixIncrement postfixIncrement;
NodePostfixDecrement postfixDecrement;
NodeImport import;
};
TOY_API void freeASTNode(ASTNode* node);
+1348
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "interpreter.h"
int _index(Interpreter* interpreter, LiteralArray* arguments);
int _set(Interpreter* interpreter, LiteralArray* arguments);
int _get(Interpreter* interpreter, LiteralArray* arguments);
int _push(Interpreter* interpreter, LiteralArray* arguments);
int _pop(Interpreter* interpreter, LiteralArray* arguments);
int _length(Interpreter* interpreter, LiteralArray* arguments);
int _clear(Interpreter* interpreter, LiteralArray* arguments);
+1177
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "toy_common.h"
#include "opcodes.h"
#include "ast_node.h"
#include "literal_array.h"
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
typedef struct Compiler {
LiteralArray literalCache;
unsigned char* bytecode;
int capacity;
int count;
} Compiler;
TOY_API void initCompiler(Compiler* compiler);
TOY_API void writeCompiler(Compiler* compiler, ASTNode* node);
TOY_API void freeCompiler(Compiler* compiler);
//embed the header, data section, code section, function section, etc.
TOY_API unsigned char* collateCompiler(Compiler* compiler, int* size);
+30
View File
@@ -0,0 +1,30 @@
#pragma once
//NOTE: you need both font AND background for these to work
//fonts color
#define FONT_BLACK "\033[30;"
#define FONT_RED "\033[31;"
#define FONT_GREEN "\033[32;"
#define FONT_YELLOW "\033[33;"
#define FONT_BLUE "\033[34;"
#define FONT_PURPLE "\033[35;"
#define FONT_DGREEN "\033[6;"
#define FONT_WHITE "\033[7;"
#define FONT_CYAN "\x1b[36m"
//background color
#define BACK_BLACK "40m"
#define BACK_RED "41m"
#define BACK_GREEN "42m"
#define BACK_YELLOW "43m"
#define BACK_BLUE "44m"
#define BACK_PURPLE "45m"
#define BACK_DGREEN "46m"
#define BACK_WHITE "47m"
//useful
#define NOTICE FONT_GREEN BACK_BLACK
#define WARN FONT_YELLOW BACK_BLACK
#define ERROR FONT_RED BACK_BLACK
#define RESET "\033[0m"
+2451
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
typedef void (*PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions
typedef struct Interpreter {
//input
unsigned char* bytecode;
int length;
int count;
int codeStart; //BUGFIX: for jumps, must be initialized to -1
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
//operation
Scope* scope;
LiteralArray stack;
//Library APIs
LiteralDictionary* hooks;
//debug outputs
PrintFn printOutput;
PrintFn assertOutput;
PrintFn errorOutput;
int depth; //don't overflow
bool panic;
} Interpreter;
//native API
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
TOY_API bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* arguments, LiteralArray* returns);
TOY_API bool callFn(Interpreter* interpreter, char* name, LiteralArray* arguments, LiteralArray* returns);
//utilities for the host program
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
TOY_API void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput);
TOY_API void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
TOY_API void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
//main access
TOY_API void initInterpreter(Interpreter* interpreter); //start of program
TOY_API void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
TOY_API void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
TOY_API void freeInterpreter(Interpreter* interpreter); //end of program
+77
View File
@@ -0,0 +1,77 @@
#include "keyword_types.h"
#include "toy_common.h"
#include <string.h>
KeywordType keywordTypes[] = {
//type keywords
{TOKEN_NULL, "null"},
{TOKEN_BOOLEAN, "bool"},
{TOKEN_INTEGER, "int"},
{TOKEN_FLOAT, "float"},
{TOKEN_STRING, "string"},
{TOKEN_FUNCTION, "fn"},
{TOKEN_OPAQUE, "opaque"},
{TOKEN_ANY, "any"},
//other keywords
{TOKEN_AS, "as"},
{TOKEN_ASSERT, "assert"},
{TOKEN_BREAK, "break"},
{TOKEN_CLASS, "class"},
{TOKEN_CONST, "const"},
{TOKEN_CONTINUE, "continue"},
{TOKEN_DO, "do"},
{TOKEN_ELSE, "else"},
{TOKEN_EXPORT, "export"},
{TOKEN_FOR, "for"},
{TOKEN_FOREACH, "foreach"},
{TOKEN_IF, "if"},
{TOKEN_IMPORT, "import"},
{TOKEN_IN, "in"},
{TOKEN_OF, "of"},
{TOKEN_PRINT, "print"},
{TOKEN_RETURN, "return"},
{TOKEN_TYPE, "type"},
{TOKEN_ASTYPE, "astype"},
{TOKEN_TYPEOF, "typeof"},
{TOKEN_VAR, "var"},
{TOKEN_WHILE, "while"},
//literal values
{TOKEN_LITERAL_TRUE, "true"},
{TOKEN_LITERAL_FALSE, "false"},
//meta tokens
{TOKEN_PASS, NULL},
{TOKEN_ERROR, NULL},
{TOKEN_EOF, NULL},
};
char* findKeywordByType(TokenType type) {
if (type == TOKEN_EOF) {
return "EOF";
}
for(int i = 0; keywordTypes[i].keyword; i++) {
if (keywordTypes[i].type == type) {
return keywordTypes[i].keyword;
}
}
return NULL;
}
TokenType findTypeByKeyword(const char* keyword) {
const int length = strlen(keyword);
for (int i = 0; keywordTypes[i].keyword; i++) {
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
return keywordTypes[i].type;
}
}
return TOKEN_EOF;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "token_types.h"
typedef struct {
TokenType type;
char* keyword;
} KeywordType;
extern KeywordType keywordTypes[];
char* findKeywordByType(TokenType type);
TokenType findTypeByKeyword(const char* keyword);
+348
View File
@@ -0,0 +1,348 @@
#include "lexer.h"
#include "console_colors.h"
#include "keyword_types.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//static generic utility functions
static void cleanLexer(Lexer* lexer) {
lexer->source = NULL;
lexer->start = 0;
lexer->current = 0;
lexer->line = 1;
}
static bool isAtEnd(Lexer* lexer) {
return lexer->source[lexer->current] == '\0';
}
static char peek(Lexer* lexer) {
return lexer->source[lexer->current];
}
static char peekNext(Lexer* lexer) {
if (isAtEnd(lexer)) return '\0';
return lexer->source[lexer->current + 1];
}
static char advance(Lexer* lexer) {
if (isAtEnd(lexer)) {
return '\0';
}
//new line
if (lexer->source[lexer->current] == '\n') {
lexer->line++;
}
lexer->current++;
return lexer->source[lexer->current - 1];
}
static void eatWhitespace(Lexer* lexer) {
const char c = peek(lexer);
switch(c) {
case ' ':
case '\r':
case '\n':
case '\t':
advance(lexer);
break;
//comments
case '/':
//eat the line
if (peekNext(lexer) == '/') {
while (advance(lexer) != '\n' && !isAtEnd(lexer));
break;
}
//eat the block
if (peekNext(lexer) == '*') {
advance(lexer);
advance(lexer);
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
advance(lexer);
advance(lexer);
break;
}
return;
default:
return;
}
//tail recursion
eatWhitespace(lexer);
}
static bool isDigit(Lexer* lexer) {
return peek(lexer) >= '0' && peek(lexer) <= '9';
}
static bool isAlpha(Lexer* lexer) {
return
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
peek(lexer) == '_'
;
}
static bool match(Lexer* lexer, char c) {
if (peek(lexer) == c) {
advance(lexer);
return true;
}
return false;
}
//token generators
static Token makeErrorToken(Lexer* lexer, char* msg) {
Token token;
token.type = TOKEN_ERROR;
token.lexeme = msg;
token.length = strlen(msg);
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("err:");
printToken(&token);
}
#endif
return token;
}
static Token makeToken(Lexer* lexer, TokenType type) {
Token token;
token.type = type;
token.length = lexer->current - lexer->start;
token.lexeme = &lexer->source[lexer->current - token.length];
token.line = lexer->line;
#ifndef TOY_EXPORT
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
if (command.verbose) {
printf("tok:");
printToken(&token);
}
#endif
return token;
}
static Token makeIntegerOrFloat(Lexer* lexer) {
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
while(isDigit(lexer)) advance(lexer);
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
type = TOKEN_LITERAL_FLOAT;
advance(lexer);
while(isDigit(lexer)) advance(lexer);
}
Token token;
token.type = type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
if (type == TOKEN_LITERAL_INTEGER) {
printf("int:");
} else {
printf("flt:");
}
printToken(&token);
}
#endif
return token;
}
static Token makeString(Lexer* lexer, char terminator) {
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
advance(lexer);
}
advance(lexer); //eat terminator
if (isAtEnd(lexer)) {
return makeErrorToken(lexer, "Unterminated string");
}
Token token;
token.type = TOKEN_LITERAL_STRING;
token.lexeme = &lexer->source[lexer->start + 1];
token.length = lexer->current - lexer->start - 2;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("str:");
printToken(&token);
}
#endif
return token;
}
static Token makeKeywordOrIdentifier(Lexer* lexer) {
advance(lexer); //first letter can only be alpha
while(isDigit(lexer) || isAlpha(lexer)) {
advance(lexer);
}
//scan for a keyword
for (int i = 0; keywordTypes[i].keyword; i++) {
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
Token token;
token.type = keywordTypes[i].type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("kwd:");
printToken(&token);
}
#endif
return token;
}
}
//return an identifier
Token token;
token.type = TOKEN_IDENTIFIER;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("idf:");
printToken(&token);
}
#endif
return token;
}
//exposed functions
void initLexer(Lexer* lexer, char* source) {
cleanLexer(lexer);
lexer->source = source;
}
Token scanLexer(Lexer* lexer) {
eatWhitespace(lexer);
lexer->start = lexer->current;
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
char c = advance(lexer);
switch(c) {
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
case '[': return makeToken(lexer, TOKEN_BRACKET_LEFT);
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
case '&': //TOKEN_AND not used
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
} else {
return makeToken(lexer, TOKEN_AND);
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
case ':': return makeToken(lexer, TOKEN_COLON);
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
case ',': return makeToken(lexer, TOKEN_COMMA);
case '.':
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
advance(lexer);
advance(lexer);
return makeToken(lexer, TOKEN_REST);
}
return makeToken(lexer, TOKEN_DOT);
case '"':
return makeString(lexer, c);
//TODO: possibly support interpolated strings
default: {
char buffer[128];
snprintf(buffer, 128, "Unexpected token: %c", c);
return makeErrorToken(lexer, buffer);
}
}
}
static void trim(char** s, int* l) { //all this to remove a newline?
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
}
//for debugging
void printToken(Token* token) {
if (token->type == TOKEN_ERROR) {
printf(ERROR "Error\t%d\t%.*s\n" RESET, token->line, token->length, token->lexeme);
return;
}
printf("\t%d\t%d\t", token->type, token->line);
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
printf("%.*s\t", token->length, token->lexeme);
} else {
char* keyword = findKeywordByType(token->type);
if (keyword != NULL) {
printf("%s", keyword);
} else {
char* str = token->lexeme;
int length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
}
}
printf("\n");
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "toy_common.h"
#include "token_types.h"
//lexers are bound to a string of code, and return a single token every time scan is called
typedef struct {
char* source;
int start; //start of the token
int current; //current position of the lexer
int line; //track this for error handling
} Lexer;
//tokens are intermediaries between lexers and parsers
typedef struct {
TokenType type;
char* lexeme;
int length;
int line;
} Token;
TOY_API void initLexer(Lexer* lexer, char* source);
Token scanLexer(Lexer* lexer);
//for debugging
void printToken(Token* token);
+701
View File
@@ -0,0 +1,701 @@
#include "literal.h"
#include "memory.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
#include "console_colors.h"
#include <stdio.h>
//hash util functions
static unsigned int hashString(const char* string, int length) {
unsigned int hash = 2166136261u;
for (int i = 0; i < length; i++) {
hash *= string[i];
hash ^= 16777619;
}
return hash;
}
static unsigned int hashUInt(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
//exposed functions
void freeLiteral(Literal literal) {
//refstrings
if (IS_STRING(literal)) {
deleteRefString(AS_STRING(literal));
return;
}
if (IS_IDENTIFIER(literal)) {
deleteRefString(AS_IDENTIFIER(literal));
return;
}
//compounds
if (IS_ARRAY(literal) || literal.type == LITERAL_DICTIONARY_INTERMEDIATE || literal.type == LITERAL_TYPE_INTERMEDIATE) {
freeLiteralArray(AS_ARRAY(literal));
FREE(LiteralArray, AS_ARRAY(literal));
return;
}
if (IS_DICTIONARY(literal)) {
freeLiteralDictionary(AS_DICTIONARY(literal));
FREE(LiteralDictionary, AS_DICTIONARY(literal));
return;
}
//complex literals
if (IS_FUNCTION(literal)) {
popScope(AS_FUNCTION(literal).scope);
AS_FUNCTION(literal).scope = NULL;
FREE_ARRAY(unsigned char, AS_FUNCTION(literal).bytecode, AS_FUNCTION(literal).length);
}
if (IS_TYPE(literal)) {
for (int i = 0; i < AS_TYPE(literal).count; i++) {
freeLiteral(((Literal*)(AS_TYPE(literal).subtypes))[i]);
}
FREE_ARRAY(Literal, AS_TYPE(literal).subtypes, AS_TYPE(literal).capacity);
return;
}
}
bool _isTruthy(Literal x) {
if (IS_NULL(x)) {
fprintf(stderr, ERROR "ERROR: Null is neither true nor false\n" RESET);
return false;
}
if (IS_BOOLEAN(x)) {
return AS_BOOLEAN(x);
}
return true;
}
Literal _toStringLiteral(RefString* ptr) {
return ((Literal){LITERAL_STRING, { .string.ptr = ptr }});
}
Literal _toIdentifierLiteral(RefString* ptr) {
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = ptr, .identifier.hash = hashString(toCString(ptr), lengthRefString(ptr)) }});
}
Literal* _typePushSubtype(Literal* lit, Literal subtype) {
//grow the subtype array
if (AS_TYPE(*lit).count + 1 > AS_TYPE(*lit).capacity) {
int oldCapacity = AS_TYPE(*lit).capacity;
AS_TYPE(*lit).capacity = GROW_CAPACITY(oldCapacity);
AS_TYPE(*lit).subtypes = GROW_ARRAY(Literal, AS_TYPE(*lit).subtypes, oldCapacity, AS_TYPE(*lit).capacity);
}
//actually push
((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count++ ] = subtype;
return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ];
}
Literal copyLiteral(Literal original) {
switch(original.type) {
case LITERAL_NULL:
case LITERAL_BOOLEAN:
case LITERAL_INTEGER:
case LITERAL_FLOAT:
//no copying needed
return original;
case LITERAL_STRING: {
return TO_STRING_LITERAL(copyRefString(AS_STRING(original)));
}
case LITERAL_ARRAY: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
//copy each element
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
pushLiteralArray(array, AS_ARRAY(original)->literals[i]);
}
return TO_ARRAY_LITERAL(array);
}
case LITERAL_DICTIONARY: {
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
//copy each entry
for (int i = 0; i < AS_DICTIONARY(original)->capacity; i++) {
if ( !IS_NULL(AS_DICTIONARY(original)->entries[i].key) ) {
setLiteralDictionary(dictionary, AS_DICTIONARY(original)->entries[i].key, AS_DICTIONARY(original)->entries[i].value);
}
}
return TO_DICTIONARY_LITERAL(dictionary);
}
case LITERAL_FUNCTION: {
unsigned char* buffer = ALLOCATE(unsigned char, AS_FUNCTION(original).length);
memcpy(buffer, AS_FUNCTION(original).bytecode, AS_FUNCTION(original).length);
Literal literal = TO_FUNCTION_LITERAL(buffer, AS_FUNCTION(original).length);
AS_FUNCTION(literal).scope = copyScope(AS_FUNCTION(original).scope);
return literal;
}
case LITERAL_IDENTIFIER: {
return TO_IDENTIFIER_LITERAL(copyRefString(AS_IDENTIFIER(original)));
}
case LITERAL_TYPE: {
Literal lit = TO_TYPE_LITERAL(AS_TYPE(original).typeOf, AS_TYPE(original).constant);
for (int i = 0; i < AS_TYPE(original).count; i++) {
TYPE_PUSH_SUBTYPE(&lit, copyLiteral( ((Literal*)(AS_TYPE(original).subtypes))[i] ));
}
return lit;
}
case LITERAL_OPAQUE: {
return original; //literally a shallow copy
}
case LITERAL_DICTIONARY_INTERMEDIATE: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
//copy each element
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
pushLiteralArray(array, literal);
freeLiteral(literal);
}
Literal ret = TO_ARRAY_LITERAL(array);
ret.type = LITERAL_DICTIONARY_INTERMEDIATE;
return ret;
}
case LITERAL_TYPE_INTERMEDIATE: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
//copy each element
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
pushLiteralArray(array, literal);
freeLiteral(literal);
}
Literal ret = TO_ARRAY_LITERAL(array);
ret.type = LITERAL_TYPE_INTERMEDIATE;
return ret;
}
case LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
case LITERAL_FUNCTION_NATIVE:
//no copying possible
return original;
default:
fprintf(stderr, ERROR "ERROR: Can't copy that literal type: %d\n" RESET, original.type);
return TO_NULL_LITERAL;
}
}
bool literalsAreEqual(Literal lhs, Literal rhs) {
//utility for other things
if (lhs.type != rhs.type) {
// ints and floats are compatible
if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) {
if (IS_INTEGER(lhs)) {
return AS_INTEGER(lhs) + AS_FLOAT(rhs);
}
else {
return AS_FLOAT(lhs) + AS_INTEGER(rhs);
}
}
return false;
}
switch(lhs.type) {
case LITERAL_NULL:
return true; //can only be true because of the check above
case LITERAL_BOOLEAN:
return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs);
case LITERAL_INTEGER:
return AS_INTEGER(lhs) == AS_INTEGER(rhs);
case LITERAL_FLOAT:
return AS_FLOAT(lhs) == AS_FLOAT(rhs);
case LITERAL_STRING:
return equalsRefString(AS_STRING(lhs), AS_STRING(rhs));
case LITERAL_ARRAY:
case LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
//mismatched sizes
if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) {
return false;
}
//mismatched elements (in order)
for (int i = 0; i < AS_ARRAY(lhs)->count; i++) {
if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) {
return false;
}
}
return true;
case LITERAL_DICTIONARY:
//relatively slow, especially when nested
for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) {
if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
//check it exists in rhs
if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) {
return false;
}
//compare the values
Literal val = getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, val)) {
freeLiteral(val);
return false;
}
freeLiteral(val);
}
}
return true;
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
return false; //functions are never equal
break;
case LITERAL_IDENTIFIER:
//check shortcuts
if (HASH_I(lhs) != HASH_I(rhs)) {
return false;
}
return equalsRefString(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs));
case LITERAL_TYPE:
//check types
if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) {
return false;
}
//const don't match
if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) {
return false;
}
//check subtypes
if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) {
return false;
}
//check array|dictionary signatures are the same (in order)
if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) {
for (int i = 0; i < AS_TYPE(lhs).count; i++) {
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) {
return false;
}
}
}
return true;
case LITERAL_OPAQUE:
return false; //IDK what this is!
case LITERAL_ANY:
return true;
case LITERAL_FUNCTION_INTERMEDIATE:
fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET);
return false;
default:
//should never be seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type);
return false;
}
return false;
}
int hashLiteral(Literal lit) {
switch(lit.type) {
case LITERAL_NULL:
return 0;
case LITERAL_BOOLEAN:
return AS_BOOLEAN(lit) ? 1 : 0;
case LITERAL_INTEGER:
return hashUInt((unsigned int)AS_INTEGER(lit));
case LITERAL_FLOAT:
return hashUInt(*(unsigned int*)(&AS_FLOAT(lit)));
case LITERAL_STRING:
return hashString(toCString(AS_STRING(lit)), lengthRefString(AS_STRING(lit)));
case LITERAL_ARRAY: {
unsigned int res = 0;
for (int i = 0; i < AS_ARRAY(lit)->count; i++) {
res += hashLiteral(AS_ARRAY(lit)->literals[i]);
}
return hashUInt(res);
}
case LITERAL_DICTIONARY: {
unsigned int res = 0;
for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) {
if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key);
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value);
}
}
return hashUInt(res);
}
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
return 0; //can't hash these
case LITERAL_IDENTIFIER:
return HASH_I(lit); //pre-computed
case LITERAL_TYPE:
return AS_TYPE(lit).typeOf; //nothing else I can do
case LITERAL_OPAQUE:
case LITERAL_ANY:
return -1;
default:
//should never bee seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type);
return 0;
}
}
//utils
static void stdoutWrapper(const char* output) {
printf("%s", output);
}
//buffer the prints
static char* globalPrintBuffer = NULL;
static size_t globalPrintCapacity = 0;
static size_t globalPrintCount = 0;
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
static char quotes = 0; //set to 0 to not show string quotes
static void printToBuffer(const char* str) {
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
int oldCapacity = globalPrintCapacity;
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
globalPrintCount += strlen(str);
}
//exposed functions
void printLiteral(Literal literal) {
printLiteralCustom(literal, stdoutWrapper);
}
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
switch(literal.type) {
case LITERAL_NULL:
printFn("null");
break;
case LITERAL_BOOLEAN:
printFn(AS_BOOLEAN(literal) ? "true" : "false");
break;
case LITERAL_INTEGER: {
char buffer[256];
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
printFn(buffer);
}
break;
case LITERAL_FLOAT: {
char buffer[256];
if (AS_FLOAT(literal) - (int)AS_FLOAT(literal)) {
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
}
else {
snprintf(buffer, 256, "%.1f", AS_FLOAT(literal));
}
printFn(buffer);
}
break;
case LITERAL_STRING: {
char buffer[MAX_STRING_LENGTH];
if (!quotes) {
snprintf(buffer, MAX_STRING_LENGTH, "%.*s", lengthRefString(AS_STRING(literal)), toCString(AS_STRING(literal)));
}
else {
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, lengthRefString(AS_STRING(literal)), toCString(AS_STRING(literal)), quotes);
}
printFn(buffer);
}
break;
case LITERAL_ARRAY: {
LiteralArray* ptr = AS_ARRAY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
printToBuffer("[");
for (int i = 0; i < ptr->count; i++) {
quotes = '"';
printLiteralCustom(ptr->literals[i], printToBuffer);
if (i + 1 < ptr->count) {
printToBuffer(",");
}
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case LITERAL_DICTIONARY: {
LiteralDictionary* ptr = AS_DICTIONARY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
int delimCount = 0;
printToBuffer("[");
for (int i = 0; i < ptr->capacity; i++) {
if (IS_NULL(ptr->entries[i].key)) {
continue;
}
if (delimCount++ > 0) {
printToBuffer(",");
}
quotes = '"';
printLiteralCustom(ptr->entries[i].key, printToBuffer);
printToBuffer(":");
quotes = '"';
printLiteralCustom(ptr->entries[i].value, printToBuffer);
}
//empty dicts MUST have a ":" printed
if (ptr->count == 0) {
printToBuffer(":");
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
printFn("(function)");
break;
case LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", lengthRefString(AS_IDENTIFIER(literal)), toCString(AS_IDENTIFIER(literal)));
printFn(buffer);
}
break;
case LITERAL_TYPE: {
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the type correctly
printToBuffer("<");
switch(AS_TYPE(literal).typeOf) {
case LITERAL_NULL:
printToBuffer("null");
break;
case LITERAL_BOOLEAN:
printToBuffer("bool");
break;
case LITERAL_INTEGER:
printToBuffer("int");
break;
case LITERAL_FLOAT:
printToBuffer("float");
break;
case LITERAL_STRING:
printToBuffer("string");
break;
case LITERAL_ARRAY:
//print all in the array
printToBuffer("[");
for (int i = 0; i < AS_TYPE(literal).count; i++) {
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
}
printToBuffer("]");
break;
case LITERAL_DICTIONARY:
printToBuffer("[");
for (int i = 0; i < AS_TYPE(literal).count; i += 2) {
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
printToBuffer(":");
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
}
printToBuffer("]");
break;
case LITERAL_FUNCTION:
printToBuffer("function");
break;
case LITERAL_FUNCTION_NATIVE:
printToBuffer("native");
break;
case LITERAL_IDENTIFIER:
printToBuffer("identifier");
break;
case LITERAL_TYPE:
printToBuffer("type");
break;
case LITERAL_OPAQUE:
printToBuffer("opaque");
break;
case LITERAL_ANY:
printToBuffer("any");
break;
default:
//should never be seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf);
}
//const (printed last)
if (AS_TYPE(literal).constant) {
printToBuffer(" const");
}
printToBuffer(">");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case LITERAL_TYPE_INTERMEDIATE:
case LITERAL_FUNCTION_INTERMEDIATE:
printFn("Unprintable literal found");
break;
case LITERAL_OPAQUE:
printFn("(opaque)");
break;
case LITERAL_ANY:
printFn("(any)");
break;
default:
//should never be seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type);
}
}
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include "toy_common.h"
#include "refstring.h"
#include <string.h>
typedef enum {
LITERAL_NULL,
LITERAL_BOOLEAN,
LITERAL_INTEGER,
LITERAL_FLOAT,
LITERAL_STRING,
LITERAL_ARRAY,
LITERAL_DICTIONARY,
LITERAL_FUNCTION,
LITERAL_IDENTIFIER,
LITERAL_TYPE,
LITERAL_OPAQUE,
LITERAL_ANY,
//these are meta-level types - not for general use
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
LITERAL_FUNCTION_NATIVE, //for handling native functions only
} LiteralType;
typedef struct {
LiteralType type;
union {
bool boolean;
int integer;
float number;
struct {
RefString* ptr;
//string hash?
} string;
void* array;
void* dictionary;
struct {
void* bytecode;
void* scope;
int length;
} function;
struct { //for variable names
RefString* ptr;
int hash;
} identifier;
struct {
LiteralType typeOf; //no longer a mask
bool constant;
void* subtypes; //for nested types caused by compounds
int capacity;
int count;
} type;
struct {
void* ptr;
int tag; //TODO: remove tags?
} opaque;
} as;
} Literal;
#define IS_NULL(value) ((value).type == LITERAL_NULL)
#define IS_BOOLEAN(value) ((value).type == LITERAL_BOOLEAN)
#define IS_INTEGER(value) ((value).type == LITERAL_INTEGER)
#define IS_FLOAT(value) ((value).type == LITERAL_FLOAT)
#define IS_STRING(value) ((value).type == LITERAL_STRING)
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
#define IS_OPAQUE(value) ((value).type == LITERAL_OPAQUE)
#define AS_BOOLEAN(value) ((value).as.boolean)
#define AS_INTEGER(value) ((value).as.integer)
#define AS_FLOAT(value) ((value).as.number)
#define AS_STRING(value) ((value).as.string.ptr)
#define AS_ARRAY(value) ((LiteralArray*)((value).as.array))
#define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary))
#define AS_FUNCTION(value) ((value).as.function)
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
#define AS_TYPE(value) ((value).as.type)
#define AS_OPAQUE(value) ((value).as.opaque.ptr)
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
#define TO_STRING_LITERAL(value) _toStringLiteral(value)
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
#define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value)
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
#define TO_OPAQUE_LITERAL(value, t) ((Literal){ LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
TOY_API void freeLiteral(Literal literal);
#define IS_TRUTHY(x) _isTruthy(x)
#define MAX_STRING_LENGTH 4096
#define HASH_I(lit) ((lit).as.identifier.hash)
#define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype)
#define OPAQUE_TAG(o) o.as.opaque.tag
//BUGFIX: macros are not functions
TOY_API bool _isTruthy(Literal x);
TOY_API Literal _toStringLiteral(RefString* ptr);
TOY_API Literal _toIdentifierLiteral(RefString* ptr);
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
//utils
TOY_API Literal copyLiteral(Literal original);
TOY_API bool literalsAreEqual(Literal lhs, Literal rhs);
TOY_API int hashLiteral(Literal lit);
TOY_API void printLiteral(Literal literal);
TOY_API void printLiteralCustom(Literal literal, void (printFn)(const char*));
+98
View File
@@ -0,0 +1,98 @@
#include "literal_array.h"
#include "memory.h"
#include <stdio.h>
#include <string.h>
//exposed functions
void initLiteralArray(LiteralArray* array) {
array->capacity = 0;
array->count = 0;
array->literals = NULL;
}
void freeLiteralArray(LiteralArray* array) {
//clean up memory
for(int i = 0; i < array->count; i++) {
freeLiteral(array->literals[i]);
}
FREE_ARRAY(Literal, array->literals, array->capacity);
initLiteralArray(array);
}
int pushLiteralArray(LiteralArray* array, Literal literal) {
if (array->capacity < array->count + 1) {
int oldCapacity = array->capacity;
array->capacity = GROW_CAPACITY(oldCapacity);
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
}
array->literals[array->count] = copyLiteral(literal);
return array->count++;
}
Literal popLiteralArray(LiteralArray* array) {
if (array->count <= 0) {
return TO_NULL_LITERAL;
}
//get the return
Literal ret = array->literals[array->count-1];
//null the existing data
array->literals[array->count-1] = TO_NULL_LITERAL;
array->count--;
return ret;
}
//find a literal in the array that matches the "literal" argument
int findLiteralIndex(LiteralArray* array, Literal literal) {
for (int i = 0; i < array->count; i++) {
//not the same type
if (array->literals[i].type != literal.type) {
continue;
}
//types match?
if (literalsAreEqual(array->literals[i], literal)) {
return i;
}
}
return -1;
}
bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
if (!IS_INTEGER(index)) {
return false;
}
int idx = AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return false;
}
freeLiteral(array->literals[idx]);
array->literals[idx] = copyLiteral(value);
return true;
}
Literal getLiteralArray(LiteralArray* array, Literal index) {
if (!IS_INTEGER(index)) {
return TO_NULL_LITERAL;
}
int idx = AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return TO_NULL_LITERAL;
}
return copyLiteral(array->literals[idx]);
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
typedef struct LiteralArray {
Literal* literals;
int capacity;
int count;
} LiteralArray;
TOY_API void initLiteralArray(LiteralArray* array);
TOY_API void freeLiteralArray(LiteralArray* array);
TOY_API int pushLiteralArray(LiteralArray* array, Literal literal);
TOY_API Literal popLiteralArray(LiteralArray* array);
TOY_API bool setLiteralArray(LiteralArray* array, Literal index, Literal value);
TOY_API Literal getLiteralArray(LiteralArray* array, Literal index);
int findLiteralIndex(LiteralArray* array, Literal literal);
+219
View File
@@ -0,0 +1,219 @@
#include "literal_dictionary.h"
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
//util functions
static void setEntryValues(_entry* entry, Literal key, Literal value) {
//much simpler now
freeLiteral(entry->key);
entry->key = copyLiteral(key);
freeLiteral(entry->value);
entry->value = copyLiteral(value);
}
static _entry* getEntryArray(_entry* array, int capacity, Literal key, unsigned int hash, bool mustExist) {
//find "key", starting at index
unsigned int index = hash % capacity;
unsigned int start = index;
//increment once, so it can't equal start
index = (index + 1) % capacity;
//literal probing and collision checking
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
_entry* entry = &array[index];
if (IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
if (IS_NULL(entry->value) && !mustExist) {
//found a truly empty bucket
return entry;
}
//else it's a tombstone - ignore
} else {
if (literalsAreEqual(key, entry->key)) {
return entry;
}
}
index = (index + 1) % capacity;
}
return NULL;
}
static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int capacity) {
//new entry space
_entry* newEntries = ALLOCATE(_entry, capacity);
for (int i = 0; i < capacity; i++) {
newEntries[i].key = TO_NULL_LITERAL;
newEntries[i].value = TO_NULL_LITERAL;
}
//move the old array into the new one
for (int i = 0; i < oldCapacity; i++) {
if (IS_NULL((*dictionaryHandle)[i].key)) {
continue;
}
//place the key and value in the new array (reusing string memory)
_entry* entry = getEntryArray(newEntries, capacity, TO_NULL_LITERAL, hashLiteral((*dictionaryHandle)[i].key), false);
entry->key = (*dictionaryHandle)[i].key;
entry->value = (*dictionaryHandle)[i].value;
}
//clear the old array
FREE_ARRAY(_entry, *dictionaryHandle, oldCapacity);
*dictionaryHandle = newEntries;
}
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
//expand array if needed
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
int oldCapacity = *capacityPtr;
*capacityPtr = GROW_CAPACITY(*capacityPtr);
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
}
_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
//true = contains increase
if (IS_NULL(entry->key)) {
setEntryValues(entry, key, value);
return true;
}
else {
setEntryValues(entry, key, value);
return false;
}
return false;
}
static void freeEntry(_entry* entry) {
freeLiteral(entry->key);
freeLiteral(entry->value);
entry->key = TO_NULL_LITERAL;
entry->value = TO_NULL_LITERAL;
}
static void freeEntryArray(_entry* array, int capacity) {
if (array == NULL) {
return;
}
for (int i = 0; i < capacity; i++) {
if (!IS_NULL(array[i].key)) {
freeEntry(&array[i]);
}
}
FREE_ARRAY(_entry, array, capacity);
}
//exposed functions
void initLiteralDictionary(LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = GROW_CAPACITY(0);
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
}
void freeLiteralDictionary(LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
}
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
return;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
return;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
return;
}
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
if (increment) {
dictionary->contains++;
dictionary->count++;
}
}
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
if (entry != NULL) {
return copyLiteral(entry->value);
}
else {
return TO_NULL_LITERAL;
}
}
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (remove)\n" RESET);
return;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (remove)\n" RESET);
return;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
return;
}
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
if (entry != NULL) {
freeEntry(entry);
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
dictionary->count--;
}
}
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
//null & not tombstoned
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
//TODO: benchmark this
#define DICTIONARY_MAX_LOAD 0.75
typedef struct _entry {
Literal key;
Literal value;
} _entry;
typedef struct LiteralDictionary {
_entry* entries;
int capacity;
int count;
int contains; //count + tombstones, for internal use
} LiteralDictionary;
TOY_API void initLiteralDictionary(LiteralDictionary* dictionary);
TOY_API void freeLiteralDictionary(LiteralDictionary* dictionary);
TOY_API void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
TOY_API Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
TOY_API void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
TOY_API bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);
+37 -42
View File
@@ -1,59 +1,54 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
#directories
SRC_ROOTDIR=..
SRC_SOURCEDIR=.
IDIR+=.
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=
SRC_OUTDIR=$(SRC_ROOTDIR)/$(TOY_OUTDIR)
SRC_OBJDIR=$(TOY_OBJDIR)
ODIR = obj
SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
#file names
SRC_SOURCEFILES=$(wildcard $(SRC_SOURCEDIR)/*.c)
SRC_OBJFILES=$(addprefix $(SRC_OBJDIR)/,$(notdir $(SRC_SOURCEFILES:.c=.o)))
SRC_TARGETNAME=Toy
OUTNAME=toy
#SRC_LIBLINE is a fancy way of making the linker work correctly
ifeq ($(shell uname),Linux)
SRC_TARGETEXT=.so
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive
CFLAGS+=-fPIC
else ifeq ($(shell uname),NetBSD)
SRC_TARGETEXT=.so
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive
CFLAGS+=-fPIC
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Linux)
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
CFLAGS += -fPIC
else ifeq ($(OS),Windows_NT)
SRC_TARGETEXT=.dll
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive -Wl,--export-all-symbols -Wl,--enable-auto-import
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Darwin)
SRC_TARGETEXT=.dylib
SRC_LIBLINE=-shared -Wl,-rpath,. $(SRC_OBJFILES)
LIBLINE = $(OBJ)
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).dylib
else
@echo "Platform test failed - what platform is this?"
exit 1
endif
#build the object files, compile the test cases, and run
all: build link
library: $(OBJ)
$(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE)
#targets for each step
.PHONY: build
build: $(SRC_OUTDIR) $(SRC_OBJDIR) $(SRC_OBJFILES)
static: $(OBJ)
ar crs ../$(TOY_OUTDIR)/lib$(OUTNAME).a $(OBJ)
.PHONY: link
link: $(SRC_OUTDIR)
$(CC) -DTOY_EXPORT $(CFLAGS) -o $(SRC_OUTDIR)/lib$(SRC_TARGETNAME)$(SRC_TARGETEXT) $(SRC_LIBLINE)
library-release: $(OBJ) library
strip $(OUT)
#util targets
$(SRC_OUTDIR):
mkdir $(SRC_OUTDIR)
static-release: $(OBJ) static
strip -d ../$(TOY_OUTDIR)/lib$(OUTNAME).a
$(SRC_OBJDIR):
mkdir $(SRC_OBJDIR)
$(OBJ): | $(ODIR)
#compilation steps
$(SRC_OBJDIR)/%.o: $(SRC_SOURCEDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(SRC_SOURCEDIR)) $(CFLAGS)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) $(ODIR)
+58
View File
@@ -0,0 +1,58 @@
#include "memory.h"
#include "refstring.h"
#include "console_colors.h"
#include <stdio.h>
#include <stdlib.h>
//default allocator
void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
if (newSize == 0 && oldSize == 0) {
//causes issues, so just skip out with a NO-OP
return NULL;
}
if (newSize == 0) {
free(pointer);
return NULL;
}
void* mem = realloc(pointer, newSize);
if (mem == NULL) {
fprintf(stderr, ERROR "[internal] Memory allocation error (requested %d for %ld, replacing %d)\n" RESET, (int)newSize, (long int)pointer, (int)oldSize);
exit(-1);
}
return mem;
}
//static variables
static MemoryAllocatorFn allocator;
//preload
static void __attribute__((constructor)) preloadMemoryAllocator() {
setMemoryAllocator(defaultMemoryAllocator);
}
//exposed API
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
return allocator(pointer, oldSize, newSize);
}
void setMemoryAllocator(MemoryAllocatorFn fn) {
if (fn == NULL) {
fprintf(stderr, ERROR "[internal] Memory allocator error (can't be null)\n" RESET);
exit(-1);
}
if (fn == reallocate) {
fprintf(stderr, ERROR "[internal] Memory allocator error (can't loop the reallocate function)\n" RESET);
exit(-1);
}
allocator = fn;
setRefStringAllocatorFn(fn);
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "toy_common.h"
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
#define GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
//implementation details
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
//assign the memory allocator
typedef void* (*MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
TOY_API void setMemoryAllocator(MemoryAllocatorFn);
+84
View File
@@ -0,0 +1,84 @@
#pragma once
typedef enum Opcode {
OP_EOF,
//basic statements
OP_ASSERT,
OP_PRINT,
//data
OP_LITERAL,
OP_LITERAL_LONG, //for more than 256 literals in a chunk
OP_LITERAL_RAW, //forcibly get the raw value of the literal
//arithmetic operators
OP_NEGATE,
OP_ADDITION,
OP_SUBTRACTION,
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
OP_GROUPING_BEGIN,
OP_GROUPING_END,
//variable stuff
OP_SCOPE_BEGIN,
OP_SCOPE_END,
OP_TYPE_DECL, //declare a type to be used (as a literal)
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
OP_VAR_DECL, //declare a variable to be used (as a literal)
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
OP_FN_DECL, //declare a function to be used (as a literal)
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
OP_VAR_ASSIGN, //assign to a literal
OP_VAR_ADDITION_ASSIGN,
OP_VAR_SUBTRACTION_ASSIGN,
OP_VAR_MULTIPLICATION_ASSIGN,
OP_VAR_DIVISION_ASSIGN,
OP_VAR_MODULO_ASSIGN,
OP_TYPE_CAST, //temporarily change a type of an atomic value
OP_TYPE_OF, //get the type of a variable
OP_IMPORT,
OP_EXPORT_removed,
//for indexing
OP_INDEX,
OP_INDEX_ASSIGN,
OP_INDEX_ASSIGN_INTERMEDIATE,
OP_DOT,
//comparison of values
OP_COMPARE_EQUAL,
OP_COMPARE_NOT_EQUAL,
OP_COMPARE_LESS,
OP_COMPARE_LESS_EQUAL,
OP_COMPARE_GREATER,
OP_COMPARE_GREATER_EQUAL,
OP_INVERT, //for booleans
//logical operators
OP_AND,
OP_OR,
//jumps, and conditional jumps (absolute)
OP_JUMP,
OP_IF_FALSE_JUMP,
OP_FN_CALL,
OP_FN_RETURN,
//pop the stack at the end of a complex statement
OP_POP_STACK,
//meta
OP_FN_END, //different from SECTION_END
OP_SECTION_END = 255,
//TODO: add more
} Opcode;
+1736
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "toy_common.h"
#include "lexer.h"
#include "ast_node.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
Lexer* lexer;
bool error; //I've had an error
bool panic; //I am processing an error
//track the last two outputs from the lexer
Token current;
Token previous;
} Parser;
TOY_API void initParser(Parser* parser, Lexer* lexer);
TOY_API void freeParser(Parser* parser);
TOY_API ASTNode* scanParser(Parser* parser);
+100
View File
@@ -0,0 +1,100 @@
#include "refstring.h"
#include <string.h>
#include <assert.h>
//test variable sizes based on platform (safety)
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(RefString) == 12);
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(char) == 1);
//memory allocation
static RefStringAllocatorFn allocate;
void setRefStringAllocatorFn(RefStringAllocatorFn allocator) {
allocate = allocator;
}
//API
RefString* createRefString(char* cstring) {
int length = strlen(cstring);
return createRefStringLength(cstring, length);
}
RefString* createRefStringLength(char* cstring, int length) {
//allocate the memory area (including metadata space)
RefString* refString = (RefString*)allocate(NULL, 0, sizeof(int) * 2 + sizeof(char) * length + 1);
//set the data
refString->refcount = 1;
refString->length = length;
strncpy(refString->data, cstring, refString->length);
refString->data[refString->length] = '\0'; //string terminator
return refString;
}
void deleteRefString(RefString* refString) {
if (refString->refcount > 0) {
//decrement, then check
refString->refcount--;
if (refString->refcount <= 0) {
allocate(refString, sizeof(int) * 2 + sizeof(char) * refString->length + 1, 0);
}
}
}
int countRefString(RefString* refString) {
return refString->refcount;
}
int lengthRefString(RefString* refString) {
return refString->length;
}
RefString* copyRefString(RefString* refString) {
//Cheaty McCheater Face
refString->refcount++;
return refString;
}
RefString* deepCopyRefString(RefString* refString) {
//create a new string, with a new refcount
return createRefStringLength(refString->data, refString->length);
}
char* toCString(RefString* refString) {
return refString->data;
}
bool equalsRefString(RefString* lhs, RefString* rhs) {
//same pointer
if (lhs == rhs) {
return true;
}
//different length
if (lhs->length != rhs->length) {
return false;
}
//same string
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
}
bool equalsRefStringCString(RefString* lhs, char* cstring) {
//get the rhs length
int length = strlen(cstring);
//different length
if (lhs->length != length) {
return false;
}
//same string
return strncmp(lhs->data, cstring, lhs->length) == 0;
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
//memory allocation hook
typedef void* (*RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
void setRefStringAllocatorFn(RefStringAllocatorFn);
//the RefString structure
typedef struct RefString {
int refcount;
int length;
char data[1];
} RefString;
//API
RefString* createRefString(char* cstring);
RefString* createRefStringLength(char* cstring, int length);
void deleteRefString(RefString* refString);
int countRefString(RefString* refString);
int lengthRefString(RefString* refString);
RefString* copyRefString(RefString* refString);
RefString* deepCopyRefString(RefString* refString);
char* toCString(RefString* refString);
bool equalsRefString(RefString* lhs, RefString* rhs);
bool equalsRefStringCString(RefString* lhs, char* cstring);
+320
View File
@@ -0,0 +1,320 @@
#include "scope.h"
#include "memory.h"
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Scope* scope) {
scope->references--;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
}
if (scope->references > 0) {
return;
}
freeLiteralDictionary(&scope->variables);
freeLiteralDictionary(&scope->types);
FREE(Scope, scope);
}
//return false if invalid type
static bool checkType(Literal typeLiteral, Literal original, Literal value, bool constCheck) {
//for constants, fail if original != value
if (constCheck && AS_TYPE(typeLiteral).constant && !literalsAreEqual(original, value)) {
return false;
}
//for any types
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
return true;
}
//don't allow null types
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
return false;
}
//always allow null values
if (IS_NULL(value)) {
return true;
}
//for each type, if a mismatch is found, return false
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY && !IS_ARRAY(value)) {
return false;
}
if (IS_ARRAY(value)) {
//check value's type
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
return false;
}
//if null, assume it's a new array variable that needs checking
if (IS_NULL(original)) {
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
if (!checkType( ((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], TO_NULL_LITERAL, AS_ARRAY(value)->literals[i], constCheck)) {
return false;
}
}
return true;
}
//check children
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
if (AS_ARRAY(original)->count <= i) {
return true; //assume new entry pushed
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(original)->literals[i], AS_ARRAY(value)->literals[i], constCheck)) {
return false;
}
}
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY && !IS_DICTIONARY(value)) {
return false;
}
if (IS_DICTIONARY(value)) {
//check value's type
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
return false;
}
//if null, assume it's a new dictionary variable that needs checking
if (IS_NULL(original)) {
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
//check the type of key and value
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], TO_NULL_LITERAL, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
return false;
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], TO_NULL_LITERAL, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
return false;
}
}
return true;
}
//check each child of value against the child of original
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
if (IS_NULL(AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
continue;
}
//find the internal child of original that matches this child of value
_entry* ptr = NULL;
for (int j = 0; j < AS_DICTIONARY(original)->capacity; j++) {
if (literalsAreEqual(AS_DICTIONARY(original)->entries[j].key, AS_DICTIONARY(value)->entries[i].key)) {
ptr = &AS_DICTIONARY(original)->entries[j];
break;
}
}
//if not found, assume it's a new entry
if (!ptr) {
continue;
}
//check the type of key and value
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], ptr->key, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
return false;
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], ptr->value, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
return false;
}
}
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FUNCTION && !IS_FUNCTION(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
return false;
}
return true;
}
//exposed functions
Scope* pushScope(Scope* ancestor) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = ancestor;
initLiteralDictionary(&scope->variables);
initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
return scope;
}
Scope* popScope(Scope* scope) {
if (scope == NULL) { //CAN pop a null
return NULL;
}
Scope* ret = scope->ancestor;
//BUGFIX: when freeing a scope, free the function's scopes manually
for (int i = 0; i < scope->variables.capacity; i++) {
//handle keys, just in case
if (IS_FUNCTION(scope->variables.entries[i].key)) {
popScope(AS_FUNCTION(scope->variables.entries[i].key).scope);
AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
}
if (IS_FUNCTION(scope->variables.entries[i].value)) {
popScope(AS_FUNCTION(scope->variables.entries[i].value).scope);
AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
}
}
freeAncestorChain(scope);
return ret;
}
Scope* copyScope(Scope* original) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = original->ancestor;
initLiteralDictionary(&scope->variables);
initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
//copy the contents of the dictionaries
for (int i = 0; i < original->variables.capacity; i++) {
if (!IS_NULL(original->variables.entries[i].key)) {
setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
}
}
for (int i = 0; i < original->types.capacity; i++) {
if (!IS_NULL(original->types.entries[i].key)) {
setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
}
}
return scope;
}
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
//don't redefine a variable within this scope
if (existsLiteralDictionary(&scope->variables, key)) {
return false;
}
//store the type, for later checking on assignment
setLiteralDictionary(&scope->types, key, type);
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
return true;
}
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
}
//return false if undefined, or can't be assigned
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
Literal original = getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
freeLiteral(typeLiteral);
freeLiteral(original);
return false;
}
//actually assign
setLiteralDictionary(&scope->variables, key, value);
freeLiteral(typeLiteral);
freeLiteral(original);
return true;
}
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = getLiteralDictionary(&scope->variables, key);
return true;
}
Literal getScopeType(Scope* scope, Literal key) {
//dead end
if (scope == NULL) {
return TO_NULL_LITERAL;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->types, key)) {
return getScopeType(scope->ancestor, key);
}
return getLiteralDictionary(&scope->types, key);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "literal_array.h"
#include "literal_dictionary.h"
typedef struct Scope {
LiteralDictionary variables; //only allow identifiers as the keys
LiteralDictionary types; //the types, indexed by identifiers
struct Scope* ancestor;
int references; //how many scopes point here
} Scope;
Scope* pushScope(Scope* scope);
Scope* popScope(Scope* scope);
Scope* copyScope(Scope* original);
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key, Literal type);
bool isDelcaredScopeVariable(Scope* scope, Literal key);
//return false if undefined
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
Literal getScopeType(Scope* scope, Literal key);
+92
View File
@@ -0,0 +1,92 @@
#pragma once
typedef enum TokenType {
//types
TOKEN_NULL,
TOKEN_BOOLEAN,
TOKEN_INTEGER,
TOKEN_FLOAT,
TOKEN_STRING,
TOKEN_ARRAY,
TOKEN_DICTIONARY,
TOKEN_FUNCTION,
TOKEN_OPAQUE,
TOKEN_ANY,
//keywords and reserved words
TOKEN_AS,
TOKEN_ASSERT,
TOKEN_BREAK,
TOKEN_CLASS,
TOKEN_CONST,
TOKEN_CONTINUE,
TOKEN_DO,
TOKEN_ELSE,
TOKEN_EXPORT,
TOKEN_FOR,
TOKEN_FOREACH,
TOKEN_IF,
TOKEN_IMPORT,
TOKEN_IN,
TOKEN_OF,
TOKEN_PRINT,
TOKEN_RETURN,
TOKEN_TYPE,
TOKEN_ASTYPE,
TOKEN_TYPEOF,
TOKEN_VAR,
TOKEN_WHILE,
//literal values
TOKEN_IDENTIFIER,
TOKEN_LITERAL_TRUE,
TOKEN_LITERAL_FALSE,
TOKEN_LITERAL_INTEGER,
TOKEN_LITERAL_FLOAT,
TOKEN_LITERAL_STRING,
//math operators
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_MULTIPLY,
TOKEN_DIVIDE,
TOKEN_MODULO,
TOKEN_PLUS_ASSIGN,
TOKEN_MINUS_ASSIGN,
TOKEN_MULTIPLY_ASSIGN,
TOKEN_DIVIDE_ASSIGN,
TOKEN_MODULO_ASSIGN,
TOKEN_PLUS_PLUS,
TOKEN_MINUS_MINUS,
TOKEN_ASSIGN,
//logical operators
TOKEN_PAREN_LEFT,
TOKEN_PAREN_RIGHT,
TOKEN_BRACKET_LEFT,
TOKEN_BRACKET_RIGHT,
TOKEN_BRACE_LEFT,
TOKEN_BRACE_RIGHT,
TOKEN_NOT,
TOKEN_NOT_EQUAL,
TOKEN_EQUAL,
TOKEN_LESS,
TOKEN_GREATER,
TOKEN_LESS_EQUAL,
TOKEN_GREATER_EQUAL,
TOKEN_AND,
TOKEN_OR,
//other operators
TOKEN_COLON,
TOKEN_SEMICOLON,
TOKEN_COMMA,
TOKEN_DOT,
TOKEN_PIPE,
TOKEN_REST,
//meta tokens
TOKEN_PASS,
TOKEN_ERROR,
TOKEN_EOF,
} TokenType;
-35
View File
@@ -1,35 +0,0 @@
#include "toy_array.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
//if some values will be removed, free them first
if (paramArray != NULL && paramArray->count > capacity) {
for (unsigned int i = capacity; i < paramArray->count; i++) {
Toy_freeValue(paramArray->data[i]);
}
}
//if you're freeing everything, just return
if (capacity == 0) {
free(paramArray);
return NULL;
}
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity;
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
if (array == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize a 'Toy_Array' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity);
exit(-1);
}
array->capacity = capacity;
array->count = paramArray == NULL ? 0 :
(array->count > capacity ? capacity : array->count); //truncate lost data
return array;
}
-22
View File
@@ -1,22 +0,0 @@
#pragma once
#include "toy_common.h"
#include "toy_value.h"
//standard generic array
typedef struct Toy_Array { //32 | 64 BITNESS
unsigned int capacity; //4 | 4
unsigned int count; //4 | 4
Toy_Value data[]; //- | -
} Toy_Array; //8 | 8
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
//some useful sizes, could be swapped out as needed
#ifndef TOY_ARRAY_INITIAL_CAPACITY
#define TOY_ARRAY_INITIAL_CAPACITY 8
#endif
#ifndef TOY_ARRAY_EXPANSION_RATE
#define TOY_ARRAY_EXPANSION_RATE 2
#endif
-317
View File
@@ -1,317 +0,0 @@
#include "toy_ast.h"
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BLOCK;
tmp->block.innerScope = false;
tmp->block.child = NULL;
tmp->block.next = NULL;
tmp->block.tail = NULL;
(*astHandle) = tmp;
}
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child) {
//first, check if we're an empty head
if (block->block.child == NULL) {
block->block.child = child;
return; //First call on an empty head skips any memory allocations
}
//run (or jump) until we hit the current tail
Toy_Ast* iter = block->block.tail ? block->block.tail : block;
while(iter->block.next != NULL) {
iter = iter->block.next;
}
//append a new link to the chain
Toy_private_initAstBlock(bucketHandle, &(iter->block.next));
//store the child in the new link, prep the tail pointer
iter->block.next->block.child = child;
block->block.tail = iter->block.next;
}
void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VALUE;
tmp->value.value = value;
(*astHandle) = tmp;
}
void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_UNARY;
tmp->unary.flag = flag;
tmp->unary.child = *astHandle;
(*astHandle) = tmp;
}
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BINARY;
tmp->binary.flag = flag;
tmp->binary.left = *astHandle; //left-recursive
tmp->binary.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BINARY_SHORT_CIRCUIT;
tmp->binary.flag = flag;
tmp->binary.left = *astHandle; //left-recursive
tmp->binary.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_COMPARE;
tmp->compare.flag = flag;
tmp->compare.left = *astHandle; //left-recursive
tmp->compare.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_GROUP;
tmp->group.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_COMPOUND;
tmp->compound.flag = flag;
tmp->compound.child = *astHandle;
(*astHandle) = tmp;
}
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_AGGREGATE;
tmp->aggregate.flag = flag;
tmp->aggregate.left = *astHandle; //left-recursive
tmp->aggregate.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ASSERT;
tmp->assert.child = child;
tmp->assert.message = msg;
(*astHandle) = tmp;
}
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_IF_THEN_ELSE;
tmp->ifThenElse.condBranch = condBranch;
tmp->ifThenElse.thenBranch = thenBranch;
tmp->ifThenElse.elseBranch = elseBranch;
(*astHandle) = tmp;
}
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_WHILE_THEN;
tmp->whileThen.condBranch = condBranch;
tmp->whileThen.thenBranch = thenBranch;
(*astHandle) = tmp;
}
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BREAK;
(*astHandle) = tmp;
}
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_CONTINUE;
(*astHandle) = tmp;
}
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_RETURN;
tmp->fnReturn.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_PRINT;
tmp->print.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_DECLARE;
tmp->varDeclare.name = name;
tmp->varDeclare.valueType = valueType;
tmp->varDeclare.constant = constant;
tmp->varDeclare.expr = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_ASSIGN;
tmp->varAssign.flag = flag;
tmp->varAssign.target = (*astHandle);
tmp->varAssign.expr = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_ACCESS;
tmp->varAccess.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_FN_DECLARE;
tmp->fnDeclare.name = name;
tmp->fnDeclare.params = params;
tmp->fnDeclare.body = body;
(*astHandle) = tmp;
}
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* args) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_FN_INVOKE;
tmp->fnInvoke.function = (*astHandle);
tmp->fnInvoke.args = args;
(*astHandle) = tmp;
}
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ATTRIBUTE;
tmp->attribute.left = (*astHandle);
tmp->attribute.right = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_STACK_POP;
tmp->stackPop.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_PASS;
(*astHandle) = tmp;
}
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ERROR;
(*astHandle) = tmp;
}
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_END;
(*astHandle) = tmp;
}
const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
switch(type) {
case TOY_AST_BLOCK: return "BLOCK";
case TOY_AST_VALUE: return "VALUE";
case TOY_AST_UNARY: return "UNARY";
case TOY_AST_BINARY: return "BINARY";
case TOY_AST_BINARY_SHORT_CIRCUIT: return "BINARY_SHORT_CIRCUIT";
case TOY_AST_COMPARE: return "COMPARE";
case TOY_AST_GROUP: return "GROUP";
case TOY_AST_COMPOUND: return "COMPOUND";
case TOY_AST_AGGREGATE: return "AGGREGATE";
case TOY_AST_ASSERT: return "ASSERT";
case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE";
case TOY_AST_WHILE_THEN: return "WHILE_THEN";
case TOY_AST_BREAK: return "BREAK";
case TOY_AST_CONTINUE: return "CONTINUE";
case TOY_AST_RETURN: return "RETURN";
case TOY_AST_PRINT: return "PRINT";
case TOY_AST_VAR_DECLARE: return "DECLARE";
case TOY_AST_VAR_ASSIGN: return "ASSIGN";
case TOY_AST_VAR_ACCESS: return "ACCESS";
case TOY_AST_FN_DECLARE: return "FN_DECLARE";
case TOY_AST_FN_INVOKE: return "FN_INVOKE";
case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
case TOY_AST_STACK_POP: return "STACK_POP";
case TOY_AST_PASS: return "PASS";
case TOY_AST_ERROR: return "ERROR";
case TOY_AST_END: return "END";
}
return NULL;
}
-312
View File
@@ -1,312 +0,0 @@
#pragma once
#include "toy_common.h"
#include "toy_bucket.h"
#include "toy_value.h"
#include "toy_string.h"
//each major type
typedef enum Toy_AstType {
TOY_AST_BLOCK,
TOY_AST_VALUE,
TOY_AST_UNARY,
TOY_AST_BINARY,
TOY_AST_BINARY_SHORT_CIRCUIT,
TOY_AST_COMPARE,
TOY_AST_GROUP,
TOY_AST_COMPOUND,
TOY_AST_AGGREGATE,
TOY_AST_ASSERT,
TOY_AST_IF_THEN_ELSE,
TOY_AST_WHILE_THEN,
TOY_AST_BREAK,
TOY_AST_CONTINUE,
TOY_AST_RETURN,
TOY_AST_PRINT,
TOY_AST_VAR_DECLARE,
TOY_AST_VAR_ASSIGN,
TOY_AST_VAR_ACCESS,
TOY_AST_FN_DECLARE,
TOY_AST_FN_INVOKE,
TOY_AST_ATTRIBUTE,
TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements
TOY_AST_PASS,
TOY_AST_ERROR,
TOY_AST_END,
} Toy_AstType;
//flags are handled differently by different types
typedef enum Toy_AstFlag {
TOY_AST_FLAG_NONE = 0,
//binary flags
TOY_AST_FLAG_ADD = 1,
TOY_AST_FLAG_SUBTRACT = 2,
TOY_AST_FLAG_MULTIPLY = 3,
TOY_AST_FLAG_DIVIDE = 4,
TOY_AST_FLAG_MODULO = 5,
TOY_AST_FLAG_AND = 6,
TOY_AST_FLAG_OR = 7,
TOY_AST_FLAG_CONCAT = 8,
TOY_AST_FLAG_ASSIGN = 10,
TOY_AST_FLAG_ADD_ASSIGN = 11,
TOY_AST_FLAG_SUBTRACT_ASSIGN = 12,
TOY_AST_FLAG_MULTIPLY_ASSIGN = 13,
TOY_AST_FLAG_DIVIDE_ASSIGN = 14,
TOY_AST_FLAG_MODULO_ASSIGN = 15,
TOY_AST_FLAG_COMPARE_EQUAL = 20,
TOY_AST_FLAG_COMPARE_NOT = 21,
TOY_AST_FLAG_COMPARE_LESS = 22,
TOY_AST_FLAG_COMPARE_LESS_EQUAL = 23,
TOY_AST_FLAG_COMPARE_GREATER = 24,
TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25,
TOY_AST_FLAG_COMPOUND_ARRAY = 30,
TOY_AST_FLAG_COMPOUND_TABLE = 31,
TOY_AST_FLAG_COLLECTION = 32,
TOY_AST_FLAG_PAIR = 33,
TOY_AST_FLAG_INDEX = 34,
TOY_AST_FLAG_FN_ARGUMENTS = 35,
//unary flags
TOY_AST_FLAG_NEGATE = 40,
TOY_AST_FLAG_PREFIX_INCREMENT = 41,
TOY_AST_FLAG_PREFIX_DECREMENT = 42,
TOY_AST_FLAG_POSTFIX_INCREMENT = 43,
TOY_AST_FLAG_POSTFIX_DECREMENT = 44,
TOY_AST_FLAG_INVOKATION = 45,
TOY_AST_FLAG_ATTRIBUTE = 46,
// TOY_AST_FLAG_TERNARY,
} Toy_AstFlag;
//the root AST type
typedef union Toy_Ast Toy_Ast;
typedef struct Toy_AstBlock {
Toy_AstType type;
bool innerScope;
Toy_Ast* child; //begin encoding the line
Toy_Ast* next; //'next' is either an AstBlock or null
Toy_Ast* tail; //'tail' - either points to the tail of the current list, or null; only used as an optimisation in toy_ast.c
} Toy_AstBlock;
typedef struct Toy_AstValue {
Toy_AstType type;
Toy_Value value;
} Toy_AstValue;
typedef struct Toy_AstUnary {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* child;
} Toy_AstUnary;
typedef struct Toy_AstBinary {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstBinary;
typedef struct Toy_AstBinaryShortCircuit {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstBinaryShortCircuit;
typedef struct Toy_AstCompare {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstCompare;
typedef struct Toy_AstGroup {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstGroup;
typedef struct Toy_AstCompound {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* child;
} Toy_AstCompound;
typedef struct Toy_AstAggregate {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstAggregate;
typedef struct Toy_AstAssert {
Toy_AstType type;
Toy_Ast* child;
Toy_Ast* message;
} Toy_AstAssert;
typedef struct Toy_AstIfThenElse {
Toy_AstType type;
Toy_Ast* condBranch;
Toy_Ast* thenBranch;
Toy_Ast* elseBranch;
} Toy_AstIfThenElse;
typedef struct Toy_AstWhileThen {
Toy_AstType type;
Toy_Ast* condBranch;
Toy_Ast* thenBranch;
} Toy_AstWhileThen;
typedef struct Toy_AstBreak {
Toy_AstType type;
} Toy_AstBreak;
typedef struct Toy_AstContinue {
Toy_AstType type;
} Toy_AstContinue;
typedef struct Toy_AstReturn {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstReturn;
typedef struct Toy_AstPrint {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstPrint;
typedef struct Toy_AstVarDeclare {
Toy_AstType type;
Toy_String* name;
Toy_Ast* expr;
Toy_ValueType valueType;
bool constant;
} Toy_AstVarDeclare;
typedef struct Toy_AstVarAssign {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* target;
Toy_Ast* expr;
} Toy_AstVarAssign;
typedef struct Toy_AstVarAccess {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstVarAccess;
typedef struct Toy_AstFnDeclare {
Toy_AstType type;
Toy_String* name;
Toy_Ast* params;
Toy_Ast* body;
} Toy_AstFnDeclare;
typedef struct Toy_AstFnInvoke {
Toy_AstType type;
Toy_Ast* function;
Toy_Ast* args;
} Toy_AstFnInvoke;
typedef struct Toy_AstAttribute {
Toy_AstType type;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstAttribute;
typedef struct Toy_AstStackPop {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstStackPop;
typedef struct Toy_AstPass {
Toy_AstType type;
} Toy_AstPass;
typedef struct Toy_AstError {
Toy_AstType type;
} Toy_AstError;
typedef struct Toy_AstEnd {
Toy_AstType type;
} Toy_AstEnd;
union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstType type;
Toy_AstBlock block;
Toy_AstValue value;
Toy_AstUnary unary;
Toy_AstBinary binary;
Toy_AstBinaryShortCircuit binaryShortCircuit;
Toy_AstCompare compare;
Toy_AstGroup group;
Toy_AstCompound compound;
Toy_AstAggregate aggregate;
Toy_AstAssert assert;
Toy_AstIfThenElse ifThenElse;
Toy_AstWhileThen whileThen;
Toy_AstBreak breakPoint;
Toy_AstContinue continuePoint;
Toy_AstReturn fnReturn;
Toy_AstPrint print;
Toy_AstVarDeclare varDeclare;
Toy_AstVarAssign varAssign;
Toy_AstVarAccess varAccess;
Toy_AstFnDeclare fnDeclare;
Toy_AstFnInvoke fnInvoke;
Toy_AstAttribute attribute;
Toy_AstStackPop stackPop;
Toy_AstPass pass;
Toy_AstError error;
Toy_AstEnd end;
};
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child);
void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value);
void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag);
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag);
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr);
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr);
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body);
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params);
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
const char* Toy_private_getAstTypeAsCString(Toy_AstType type);
-288
View File
@@ -1,288 +0,0 @@
#include "toy_attributes.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//if set, used for delegating to user-defined code
static Toy_OpaqueAttributeHandler opaqueAttributeCallback = NULL;
//utils
#define MATCH_VALUE_AND_CSTRING(value, cstring) \
((TOY_VALUE_AS_STRING(value)->info.length == strlen(cstring)) && \
(strncmp(cstring, TOY_VALUE_AS_STRING(value)->leaf.data, TOY_VALUE_AS_STRING(value)->info.length) == 0))
//NOTE: there is no need to call 'Toy_freeValue' on the arguments, as the VM assumes you don't
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_STRING(compound)->info.length);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "asUpper")) {
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
for (int i = 0; buffer[i] != '\0'; i++) {
buffer[i] = toupper(buffer[i]);
}
Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer));
free(buffer);
return TOY_VALUE_FROM_STRING(str);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "asLower")) {
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
for (int i = 0; buffer[i] != '\0'; i++) {
buffer[i] = tolower(buffer[i]);
}
Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer));
free(buffer);
return TOY_VALUE_FROM_STRING(str);
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
}
static void attr_arrayPushBack(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value element = Toy_popStack(&vm->stack);
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
//BUGFIX: check the capacity limit
if (array->count == array->capacity) {
//correct the source value's pointer
array = Toy_resizeArray(array, array->capacity * TOY_ARRAY_EXPANSION_RATE);
if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_ARRAY) {
compound.as.reference->as.array = array;
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown error after expanding array size at %s %d", __FILE__, __LINE__);
Toy_error(buffer);
}
}
array->data[array->count] = element;
array->count++;
}
static void attr_arrayPopBack(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
//empty returns nothing
if (array->count == 0) {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
return;
}
Toy_Value element = array->data[array->count-1];
array->count--;
Toy_pushStack(&vm->stack, element);
}
static void attr_arrayForEach(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value callback = Toy_popStack(&vm->stack);
if (TOY_VALUE_IS_FUNCTION(callback) != true) {
char buffer[256];
snprintf(buffer, 256, "Expected function, found '%s'", Toy_getValueTypeAsCString(callback.type));
Toy_error(buffer);
return;
}
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
Toy_Function* fn = TOY_VALUE_AS_FUNCTION(callback);
//this emulates 'processInvoke' a bit, but not entirely
Toy_VM subVM;
Toy_inheritVM(vm, &subVM);
switch(fn->type) {
case TOY_FUNCTION_CUSTOM: {
//push and run for each element
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
//bind to the subVM (more expensive than I'd like)
Toy_bindVM(&subVM, fn->bytecode.code, fn->bytecode.parentScope);
//get parameter name as a string
unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0];
Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]);
const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr;
Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr));
Toy_declareScope(subVM.scope, Toy_copyString(name), paramType, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]), true);
Toy_freeString(name);
Toy_runVM(&subVM);
Toy_resetVM(&subVM, false, true);
subVM.scope = NULL; //BUGFIX: need to clear the scope when iterating
}
}
break;
case TOY_FUNCTION_NATIVE: {
//this uses a subVM for the native function, which is a slight difference than 'processInoke'
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
Toy_pushStack(&subVM.stack, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]));
fn->native.callback(&subVM, &fn->native); //NOTE: try not to leave anything on the stack afterwards
}
}
break;
default:
Toy_error("Can't call an unknown function type in 'forEach'");
break;
}
//cleanup
Toy_freeVM(&subVM);
}
static void attr_arraySort(Toy_VM* vm, Toy_FunctionNative* self) {
(void)vm;
(void)self;
//URGENT: attr_arraySort
}
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "pushBack")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPushBack);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "popBack")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayForEach);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "sort")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
}
static void attr_tableInsert(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value value = Toy_popStack(&vm->stack); //NOTE: the args are still backwards, except compound
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
Toy_insertTable(&table, key, value);
//BUGFIX: check the capacity limit (Toy_insertTable automatically alters the pointer value)
if (TOY_VALUE_AS_TABLE(compound) != table) {
//correct the source value's pointer
if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_TABLE) {
compound.as.reference->as.table = table;
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown error after expanding table size at %s %d", __FILE__, __LINE__);
Toy_error(buffer);
}
}
}
static void attr_tableHasKey(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(&table, key);
Toy_Value result = TOY_VALUE_FROM_BOOLEAN(entry != NULL);
Toy_pushStack(&vm->stack, result);
}
static void attr_tableRemove(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
Toy_removeTable(&table, key);
}
static void attr_tableForEach(Toy_VM* vm, Toy_FunctionNative* self) {
(void)vm;
(void)self;
//URGENT: attr_tableForEach
}
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "insert")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableInsert);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "hasKey")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableHasKey);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "remove")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableRemove);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableForEach);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
}
Toy_Value Toy_private_handleOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (opaqueAttributeCallback == NULL) {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s' (did you set the opaque callbacks?)", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
return opaqueAttributeCallback(vm, compound, attribute);
}
void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb) {
opaqueAttributeCallback = cb;
}
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include "toy_common.h"
#include "toy_value.h"
#include "toy_vm.h"
// [x] string.length
// [x] string.asUpper
// [x] string.asLower
// [x] array.length
// [x] array.pushBack(x)
// [x] array.popBack()
// [x] array.forEach(fn) // fn(x) -> void
// [ ] array.sort(fn) // fn(a,b) -> int
// [x] table.length
// [x] table.insert(x, y)
// [x] table.hasKey(x)
// [x] table.remove(x)
// [ ] table.forEach(fn) // fn(x,y) -> void
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
Toy_Value Toy_private_handleOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
//plug-and-play attributes for custom objects
typedef Toy_Value (*Toy_OpaqueAttributeHandler)(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
TOY_API void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb);
-123
View File
@@ -1,123 +0,0 @@
#include "toy_bucket.h"
#include "toy_console_colors.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//buckets of fun
Toy_Bucket* Toy_allocateBucket(unsigned int capacity) {
assert(capacity != 0 && "Cannot allocate a 'Toy_Bucket' with zero capacity");
Toy_Bucket* bucket = malloc(sizeof(Toy_Bucket) + capacity);
if (bucket == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Bucket' of %d capacity\n" TOY_CC_RESET, (int)capacity);
exit(1);
}
memset(bucket, 0, sizeof(Toy_Bucket) + capacity); //zero the memory, to avoid broken header metadata
//initialize the bucket
bucket->next = NULL;
bucket->capacity = capacity;
bucket->count = 0;
return bucket;
}
unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount) {
//the endpoint must be aligned to the word size, otherwise you'll get a bus error from moving pointers
amount = (amount + 3) & ~3; //NOTE: this also leaves the lowest two bits as zero
assert((*bucketHandle) != NULL && "Expected a 'Toy_Bucket', received NULL");
assert((*bucketHandle)->capacity >= (amount + 4) && "ERROR: Failed to partition a 'Toy_Bucket', requested amount is too high");
//if you're out of space in this bucket, allocate another one
if ((*bucketHandle)->capacity < (*bucketHandle)->count + amount + 4) { //+4 for the metadata header
Toy_Bucket* tmp = Toy_allocateBucket((*bucketHandle)->capacity);
tmp->next = (*bucketHandle); //it's buckets all the way down
(*bucketHandle) = tmp;
}
//use a 4-byte metadata header to hold the size of this partition, for GC
*((unsigned int*)((*bucketHandle)->data + (*bucketHandle)->count)) = amount;
//track the new metadata, and return the requested memory space
(*bucketHandle)->count += amount + 4;
return ((*bucketHandle)->data + (*bucketHandle)->count - amount); //metadata is before the pointer's address
}
void Toy_releaseBucketPartition(unsigned char* ptr) {
*((int*)(ptr-4)) |= 1; //flips the low-bit within the header
//no checks here, for technical reasons
}
void Toy_freeBucket(Toy_Bucket** bucketHandle) {
Toy_Bucket* iter = (*bucketHandle);
while (iter != NULL) {
//run down the chain
Toy_Bucket* last = iter;
iter = iter->next;
//clear the previous bucket from memory
free(last);
}
//for safety
(*bucketHandle) = NULL;
}
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
//clear whatever this handle is pointing to
if ((*bucketHandle) == NULL) {
return;
}
Toy_Bucket* link = *bucketHandle;
while (link) {
//find non-free partitions
unsigned char* ptr = link->data;
bool gc = true;
while (ptr - link->data < link->count) { //for each partition
if ( (*((int*)ptr) & 1) == 0) { //is this partition still in use?
gc = false;
break;
}
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
}
//free this link, if its been entirely released
if (gc) {
//if link is the head
if (link == (*bucketHandle)) {
//if there's nowhere to go, don't delete the whole bucket
if ((*bucketHandle)->next == NULL) {
return;
}
else {
(*bucketHandle) = (*bucketHandle)->next;
free(link);
link = (*bucketHandle);
}
}
else {
//find the prev and free this link before continuing
Toy_Bucket* it = (*bucketHandle);
while (it->next != link) {
it = it->next;
}
it->next = link->next;
free(link);
link = it->next;
}
}
else {
link = link->next;
}
}
}
-60
View File
@@ -1,60 +0,0 @@
#pragma once
#include "toy_common.h"
//NOTE: this is an 'arena allocator', and has restrictions on it's usage:
// - It can only expand until it is freed
// - It cannot be copied or moved around in memory
// - It cannot allocate more memory than it has 'capacity'
// If each of these rules are followed, this is actually more efficient than other options
//a custom allocator
typedef struct Toy_Bucket { //32 | 64 BITNESS
struct Toy_Bucket* next; //4 | 8
unsigned int capacity; //4 | 4
unsigned int count; //4 | 4
unsigned char data[]; //- | -
} Toy_Bucket; //12 | 16
TOY_API Toy_Bucket* Toy_allocateBucket(unsigned int capacity);
TOY_API unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount);
TOY_API void Toy_releaseBucketPartition(unsigned char* ptr);
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
//standard capacity sizes
#ifndef TOY_BUCKET_1KB
#define TOY_BUCKET_1KB (1 << 10)
#endif
#ifndef TOY_BUCKET_2KB
#define TOY_BUCKET_2KB (1 << 11)
#endif
#ifndef TOY_BUCKET_4KB
#define TOY_BUCKET_4KB (1 << 12)
#endif
#ifndef TOY_BUCKET_8KB
#define TOY_BUCKET_8KB (1 << 13)
#endif
#ifndef TOY_BUCKET_16KB
#define TOY_BUCKET_16KB (1 << 14)
#endif
#ifndef TOY_BUCKET_32KB
#define TOY_BUCKET_32KB (1 << 15)
#endif
#ifndef TOY_BUCKET_64KB
#define TOY_BUCKET_64KB (1 << 16)
#endif
//CPU L1 caches tend to be 64kb, but that's far from guaranteed
#ifndef TOY_BUCKET_IDEAL
#define TOY_BUCKET_IDEAL (TOY_BUCKET_64KB - sizeof(Toy_Bucket))
#endif
//TODO: check for leaks when freeBucket is called, for debugging
+121 -4
View File
@@ -1,8 +1,125 @@
#include "toy_common.h"
//defined separately, as compilation can take several seconds, invalidating the comparisons of the given macros
static const char* build = __DATE__ " " __TIME__ ", incomplete Toy v2.x";
#include <stdio.h>
#include <string.h>
#include <assert.h>
const char* Toy_private_versionBuild(void) {
return build;
//test variable sizes based on platform
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(char) == 1);
STATIC_ASSERT(sizeof(short) == 2);
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(float) == 4);
STATIC_ASSERT(sizeof(unsigned char) == 1);
STATIC_ASSERT(sizeof(unsigned short) == 2);
STATIC_ASSERT(sizeof(unsigned int) == 4);
#ifndef TOY_EXPORT
//declare the singleton
Command command;
void initCommand(int argc, const char* argv[]) {
//default values
command.error = false;
command.help = false;
command.version = false;
command.binaryfile = NULL;
command.sourcefile = NULL;
command.compilefile = NULL;
command.outfile = "out.tb";
command.source = NULL;
command.verbose = false;
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
command.error = true; //error state by default, set to false by successful flags
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
command.help = true;
command.error = false;
continue;
}
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
command.version = true;
command.error = false;
continue;
}
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
command.verbose = true;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
command.sourcefile = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
command.source = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
command.compilefile = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
command.outfile = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
//option without a flag + ending in .tb = binary input
if (i < argc) {
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
command.binaryfile = (char*)argv[i];
command.error = false;
continue;
}
}
//don't keep reading in an error state
return;
}
}
void usageCommand(int argc, const char* argv[]) {
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
}
void helpCommand(int argc, const char* argv[]) {
usageCommand(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");
}
void copyrightCommand(int argc, const char* argv[]) {
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
}
#endif
+37 -63
View File
@@ -1,74 +1,48 @@
#pragma once
//for specified type sizes
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdint.h>
//TOY_API is platform-dependant, and marks publicly usable API functions
#if defined(__linux__)
#define TOY_API extern
#elif defined(_WIN32) || defined(_WIN64)
#if defined(TOY_EXPORT)
#define TOY_API __declspec(dllexport)
#elif defined(TOY_IMPORT)
#define TOY_API __declspec(dllimport)
#else
#define TOY_API extern
#endif
#elif defined(__APPLE__)
#define TOY_API extern
#else
//generic solution
#define TOY_API extern
#endif
//TOY_BITNESS is used to encourage memory-cache friendliness
#if defined(__linux__)
#if defined(__LP64__)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#elif defined(__NetBSD__)
#if defined(__LP64__)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#elif defined(_WIN32) || defined(_WIN64)
#if defined(_WIN64)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#elif defined(__APPLE__)
#if defined(__LP64__)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#else
//generic solution
#define TOY_BITNESS -1
#endif
//version specifiers, embedded as the header
#define TOY_VERSION_MAJOR 2
#define TOY_VERSION_MINOR 1
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 7
#define TOY_VERSION_PATCH 0
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
//defined as a function, for technical reasons
#define TOY_VERSION_BUILD Toy_private_versionBuild()
const char* Toy_private_versionBuild(void);
//platform-specific specifications
#if defined(__linux__)
#define TOY_API extern
/*
#elif defined(_WIN32) || defined(WIN32)
#define TOY_API
Version validation rules:
#else
#define TOY_API
* Under no circumstance, should you ever run code whose major version is different from the interpreters major version
* Under no circumstance, should you ever run code whose minor version is above the interpreters minor version
* You may, at your own risk, attempt to run code whose patch version is different from the interpreters patch version
* You may, at your own risk, attempt to run code whose build version is different from the interpreters build version
#endif
*/
#ifndef TOY_EXPORT
//for processing the command line arguments
typedef struct {
bool error;
bool help;
bool version;
char* binaryfile;
char* sourcefile;
char* compilefile;
char* outfile; //defaults to out.tb
char* source;
bool verbose;
} Command;
extern Command command;
void initCommand(int argc, const char* argv[]);
void usageCommand(int argc, const char* argv[]);
void helpCommand(int argc, const char* argv[]);
void copyrightCommand(int argc, const char* argv[]);
#endif
//NOTE: assigning to a byte from a short loses data
#define AS_USHORT(value) (*(unsigned short*)(&(value)))
File diff suppressed because it is too large Load Diff
-60
View File
@@ -1,60 +0,0 @@
#pragma once
#include "toy_common.h"
#include "toy_ast.h"
//the 'escapes' are lists of data used for processing the 'break' and 'continue' keywords
typedef struct Toy_private_EscapeEntry_t {
unsigned int addr; //the address to write *to*
unsigned int depth; //the current depth
} Toy_private_EscapeEntry_t;
typedef struct Toy_private_EscapeArray {
unsigned int capacity;
unsigned int count;
Toy_private_EscapeEntry_t data[];
} Toy_private_EscapeArray;
//not needed at runtime, so they can be bigger
#ifndef TOY_ESCAPE_INITIAL_CAPACITY
#define TOY_ESCAPE_INITIAL_CAPACITY 32
#endif
#ifndef TOY_ESCAPE_EXPANSION_RATE
#define TOY_ESCAPE_EXPANSION_RATE 4
#endif
Toy_private_EscapeArray* Toy_private_resizeEscapeArray(Toy_private_EscapeArray* ptr, unsigned int capacity);
//structure for holding the bytecode during compilation
typedef struct Toy_Bytecode {
unsigned char* code; //the instruction set
unsigned int codeCapacity;
unsigned int codeCount;
unsigned char* jumps; //each 'jump' is the starting address of an element within 'data'
unsigned int jumpsCapacity;
unsigned int jumpsCount;
unsigned char* param; //each 'param' is the starting address of a name string within 'data'
unsigned int paramCapacity;
unsigned int paramCount;
unsigned char* data; //a block of read-only data
unsigned int dataCapacity;
unsigned int dataCount;
unsigned char* subs; //subroutines etc, built recursively
unsigned int subsCapacity;
unsigned int subsCount;
//tools for handling the build process
unsigned int currentScopeDepth;
Toy_private_EscapeArray* breakEscapes;
Toy_private_EscapeArray* continueEscapes;
//compilation errors
bool panic;
} Toy_Bytecode;
TOY_API unsigned char* Toy_compileToBytecode(Toy_Ast* ast);

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