Compare commits

..

111 Commits

Author SHA1 Message Date
Kayne Ruse aeb008c684 Fixed unary negation bug, removed newline from print 2023-02-10 18:38:25 +00:00
Kayne Ruse 53012dbce1 Added _filter() 2023-02-10 15:41:38 +00:00
Kayne Ruse 4fe57f9562 Added _containsKey() and _containsValue() 2023-02-10 15:27:39 +00:00
Kayne Ruse 3ba2e420ea Added _every() and _some() 2023-02-10 15:00:15 +00:00
Kayne Ruse c81a139c97 Now handles unterminated block comments without freezing 2023-02-10 12:26:38 +00:00
Kayne Ruse 66ea684a90 Disabled comments in the repl 2023-02-10 12:11:42 +00:00
Kayne Ruse a26a6a56d0 Patched a pre/postfix increment/decrement segfault 2023-02-10 11:49:59 +00:00
Kayne Ruse ee226ea426 Strengthened constness for cstrings and bytecode 2023-02-10 08:52:38 +00:00
Kayne Ruse 76a0290290 Removed export keyword from README.md 2023-02-09 17:46:28 +00:00
Kayne Ruse 85dc24c4a6 Updated bug report template 2023-02-09 17:25:24 +00:00
Kayne Ruse 1804e380a0 tweak 2023-02-09 17:06:51 +00:00
Kayne Ruse 7567f4f3b2 Experimenting with issues 2023-02-09 17:05:20 +00:00
Kayne Ruse e8160eb9df Fixed string indexing with null 2023-02-09 16:46:36 +00:00
Kayne Ruse 276648630e trying to uncluster-bomb the builtin functions 2023-02-09 16:12:42 +00:00
Kayne Ruse e946a5f071 Patched segault in parser 2023-02-09 12:37:18 +00:00
Kayne Ruse 932401c26d Can now correctly parse escaped characters 2023-02-09 08:44:08 +00:00
Kayne Ruse fc67d6a18b Fixed a segfault 2023-02-09 08:24:16 +00:00
Kayne Ruse 60908c8bf3 Added panic state to Toy_Compiler to catch a certain condition 2023-02-09 07:58:55 +00:00
Kayne Ruse b8e3324acb tweak 2023-02-07 18:32:59 +00:00
Kayne Ruse 0fc8183799 Added _reduce 2023-02-06 09:46:20 +00:00
Kayne Ruse 9a6aa8d15e Added _trimBegin() and _trimEnd() 2023-02-06 06:11:17 +00:00
Kayne Ruse 0b9051cab2 Experimenting with numbers 2023-02-06 05:37:41 +00:00
Kayne Ruse 0d7e4db661 Fixed repl bug 2023-02-06 04:47:30 +00:00
Kayne Ruse d5c833b344 I wish I could test on windows 2023-02-06 01:05:44 +00:00
Kayne Ruse c875ae7a0e Added _forEach and _map, added tests for them 2023-02-06 00:51:07 +00:00
Kayne Ruse c0ec5ef28e Tested _concat() with clashing dict keys 2023-02-05 22:46:00 +00:00
Kayne Ruse be91de9535 Fixed a leak 2023-02-05 20:46:55 +00:00
Kayne Ruse 3088c4fe6d Implemented _concat 2023-02-05 20:45:31 +00:00
Kayne Ruse 9bd0cd23e7 Imported an example file for bytecode size testing, see #21 2023-02-05 15:40:17 +00:00
Kayne Ruse 29f8a698b4 Added a mustfail test 2023-02-05 15:19:04 +00:00
Kayne Ruse 41d274177a String concatenation restricted to + and += signs 2023-02-05 15:15:32 +00:00
Kayne Ruse 7ea249f723 Added the about library, resolved #51 2023-02-05 14:43:46 +00:00
Kayne Ruse 3949be3706 Quick potential bugfix 2023-02-05 13:34:39 +00:00
Kayne Ruse 0e932f24cc Added _toString() 2023-02-05 13:29:44 +00:00
Kayne Ruse 60225b733b Added _getKeys() and _getValues() 2023-02-05 12:49:12 +00:00
Kayne Ruse 15f99495a1 Aesthetics 2023-02-05 11:31:17 +00:00
Kayne Ruse 7b26527e95 Nesting index multiplication assignment fixed
This affects all arithmetic types applied to inner-nested compounds.
2023-02-05 06:28:24 +00:00
Kayne Ruse 386201b6e9 Added fib-memo.toy, fixed bugs until it worked 2023-02-04 17:01:58 +00:00
Kayne Ruse 8d278077b1 Added constant folding for strings, tweaked some error messages 2023-02-04 09:50:29 +00:00
Kayne Ruse 57c16d2ede Fixed an obscure memory leak, potential issue with lib_runner on linux 2023-02-04 15:30:46 +11:00
Kayne Ruse 2f1613e306 Caught an error in the compiler 2023-02-04 03:03:56 +00:00
Kayne Ruse 2776c87026 Ambiguous ternary syntax might be an issue 2023-02-04 02:59:10 +00:00
Kayne Ruse 8cc33f5fbb Added to ternary expression tests 2023-02-03 17:22:44 +00:00
Kayne Ruse 90f91a234a r is a bad character to escape 2023-01-31 12:38:42 +00:00
Kayne Ruse fcd1cdf70b Added a few characters that can be escaped 2023-01-31 12:37:18 +00:00
Kayne Ruse e265038547 Tested custom char trims 2023-01-29 08:38:26 +00:00
Kayne Ruse a357e0b0d4 Implemented _trim() 2023-01-29 08:30:31 +00:00
Kayne Ruse 2c5e3c6aa5 Started working on compound library 2023-01-29 07:23:52 +00:00
Kayne Ruse aeda0a0d94 Fixed pointer to int casting 2023-01-28 09:11:38 +00:00
Kayne Ruse ebbcba1b16 Enabled -std=c18 -pedantic, fixed all resulting errors
Thanks @gyrovorbis
2023-01-28 08:54:46 +00:00
Kayne Ruse 4bce10803e Added official support for mingw32 2023-01-28 07:56:11 +00:00
Kayne Ruse daee91904d Trying to test on windows 2023-01-28 06:20:36 +00:00
Kayne Ruse c397384766 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-01-28 06:11:54 +00:00
Kayne Ruse 667efc2feb Removed nonstandard C constructor attribute 2023-01-28 06:06:04 +00:00
Kayne Ruse f4469fc53d Removed file extension guard 2023-01-27 07:03:36 +00:00
Kayne Ruse c86c5800a7 Moved guard 2023-01-27 06:31:13 +00:00
Kayne Ruse e4d843ad3a Type check 2023-01-27 06:16:39 +00:00
Kayne Ruse 6667bce3d7 Finally fixed the buffer
I suppose valgrind, et. al. complaining is a good thing, overall.
2023-01-27 06:01:05 +00:00
Kayne Ruse 50b07da188 Put the clear BEFORE the return 2023-01-27 05:56:23 +00:00
Kayne Ruse 708db75760 Forgot to free this buffer 2023-01-27 05:53:35 +00:00
Kayne Ruse b632ce77ba Moved drive to filepath code into a function 2023-01-27 05:49:14 +00:00
Kayne Ruse b2c3dd894c Added a memusage tool 2023-01-25 15:11:22 +00:00
Kayne Ruse 9b21bfb53b Missed a rename 2023-01-25 13:54:28 +00:00
Kayne Ruse d3adac8a59 Fixed the parser - whoops 2023-01-25 13:17:57 +00:00
Kayne Ruse 5183037a99 Missed the globals 2023-01-25 13:15:16 +00:00
Kayne Ruse 2e2bee4fa3 Renemed all variables to fit into a namespace
Basically, all Toy varaibles, functions, etc. are prepended with "Toy_",
and macros are prepended with "TOY_". This is to reduce namespace
pollution, which was an issue pointed out to be - blame @GyroVorbis.

I've also bumped the minor version number - theoretically I should bump
the major number, but I'm not quite ready for 1.0 yet.
2023-01-25 12:55:55 +00:00
Kayne Ruse 047ccc5f16 Prepended file names with "toy_" 2023-01-23 21:45:52 +00:00
Kayne Ruse 59b0d15915 Fixed a pattern with parsing identifiers 2023-01-23 21:19:51 +00:00
Kayne Ruse 6d961cea78 Tweaked refstring, thanks SchuperJesse! 2023-01-23 08:21:51 +00:00
Kayne Ruse 33f360c9cf Fixed a way to have a bad type, thanks neuf! 2023-01-21 13:33:25 +00:00
Kayne Ruse d3516b4fc9 Adjusted the interpreter's version guard 2023-01-21 13:10:04 +00:00
Kayne Ruse df85d30553 Added loadScriptBytecode() 2023-01-21 04:27:01 +00:00
Kayne Ruse d57fca474f Fixed memory leak 2023-01-20 23:09:01 +00:00
Kayne Ruse 69f5151310 Finished runner library 2023-01-20 23:03:00 +00:00
Kayne Ruse c47ee68b3f Don't use the runner lib yet - will finish in the morning 2023-01-20 13:59:28 +00:00
Kayne Ruse 2c06c5e38b BUGFIX: Buffer overflow 2023-01-20 13:56:07 +00:00
Kayne Ruse e6e24ca19f Began working on runner library 2023-01-20 13:42:45 +00:00
Kayne Ruse 390f60e0ce F around and find out 2023-01-16 09:48:20 +00:00
Kayne Ruse 580e407b05 Squeaky clean! 2023-01-16 09:40:32 +00:00
Kayne Ruse 26bd476c2c Sanitized some addresses 2023-01-16 09:36:05 +00:00
Kayne Ruse b8b67fce3d Testing sanitiser 2023-01-16 09:15:20 +00:00
Kayne Ruse 68ed39fc45 Began working on a few bugs, thanks /u/skeeto! 2023-01-16 09:06:35 +00:00
Kayne Ruse aeecfabbbc Null pointer exceptions in parser unary function 2023-01-15 18:15:59 +00:00
Kayne Ruse 51740e2b9e Resolved #25, Indexing an array with a non-integer causes an error 2023-01-15 15:09:01 +00:00
Kayne Ruse 402abb647c Dummied out _index 2023-01-15 14:28:53 +00:00
Kayne Ruse 3aa7bf1877 Added patron list, which I forgot until now 2023-01-15 07:24:28 +00:00
Kayne Ruse ae1dc5841e Added ternary operator, resolved #46 2023-01-14 10:24:15 +00:00
Kayne Ruse 4b60d65203 Tweaked README.md 2023-01-13 16:31:56 +00:00
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
135 changed files with 15034 additions and 11030 deletions
+5
View File
@@ -0,0 +1,5 @@
# These are supported funding model platforms
patreon: krgamestudios
ko_fi: krgamestudios
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
+29
View File
@@ -0,0 +1,29 @@
---
name: Bug Report
about: Create a report to help us improve
labels: bug
---
## Describe the bug
A clear and concise description of what the bug is.
## To Reproduce
Steps to reproduce the behaviour:
1. run `git pull` on the repository
2. run `make rebuild` on the code
3. ...
You can include some screenshots here if you'd like!
## Versioning
- OS: [for example MacOS, Windows, iOS, Android]
- Version: [What version of Toy was this running?]
### Additional context
Add any other context about the problem here.
+17
View File
@@ -0,0 +1,17 @@
---
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.
+19 -4
View File
@@ -2,18 +2,33 @@ name: Comprehensive Tests
on:
push:
branches: [ "main" ]
branches: [ "main", "dev" ]
pull_request:
branches: [ "main" ]
jobs:
build:
test-valgrind:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: install valgrind
run: sudo apt install valgrind
- name: make test
- name: make test (valgrind)
run: make test
test-sanitized:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: make test (sanitized)
run: make test-sanitized
test-mingw32:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: make test (mingw32)
run: make test
+1 -1
View File
@@ -1,6 +1,6 @@
# License
Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios
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.
+8 -8
View File
@@ -14,16 +14,14 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
* Bytecode intermediate compilation
* Optional, but robust type system (including `opaque` for arbitrary data)
* Functions and types are first-class citizens
* `import` and `export` variables from the host program
* Import external libraries
* Fancy slice notation for strings, arrays and dictionaries
* Can re-direct output, error and assertion failure messages
* Open source under the zlib license
## Building
For Windows, Linux and MacOS, simply run `make` in the root directory.
Note: For Linux, you may need to `cd` into the `out` directory before running.
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
@@ -38,7 +36,6 @@ Run `make install-tools` to install a number of tools, including:
```
import standard; //for a bunch of utility functions
print "Hello world"; //"print" is a keyword
var msg = "foobar"; //declare a variable like this
@@ -62,10 +59,13 @@ var tally = makeCounter();
print tally(); //1
print tally(); //2
print tally(); //3
export tally; //export this variable to the host program
```
# License
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
# Patrons via Patreon
* Seth A. Robinson
-51
View File
@@ -1,51 +0,0 @@
DONE: rework type system
DONE: var decl with a type, but no value
DONE: type casting
DONE: remove optimization option
DONE: conditionals
DONE: if-then-else
DONE: chained if-then-else
DONE: optional block around a path if it's only one statement
DONE: while-then
DONE: for-then
DONE: break and continue statements
DONE: truthiness rethink
DONE: string concat with the + operator
DONE: increment & decrement operators
DONE: store compound types in variables
DONE: += -= *= /= %= operators
DONE: && and || operators
DONE: functions are invoked by calling their names
DONE: function arguments can have specified types
DONE: function returns can have specified types
DONE: closures are explicitly supported
DONE: functions are first-class citizens
DONE: functions take a set number of parameters
DONE: functions last argument can be a rest parameter
DONE: assert needs to kill the whole script, not just functions
DONE: native functions
DONE: global functions _get, _set, _push, _pop, _length, clear available
DONE: change comma to colon in dictionary definition
DONE: Address circular references
DONE: are compounds shallow or deep copies? Deep copies
DONE: third output stream, for lexer/parser/compiler/interpreter errors
DONE: Assertion-based test scripts
DONE: Import/export keywords
DONE: A way to check the type of a variable (typeOf keyword)
DONE: slice and dot notation around the builtin _index and _dot functions
DONE: maximum recursion/function depth
DONE: better sugar for _push, _pop, _length
DONE: nested compound assignment bug
DONE: hooks on the external libraries, triggered on import
TODO: standard library
TODO: external script runner library
TODO: document how it all works
TODO: packaging for release?
TODO: test embedding in a game
NOPE: a = b = c = 1;
NOPE: functions return a set number of values
NOPE: ternary operator?
NOPE: Nullish types?
+24
View File
@@ -1,25 +1,49 @@
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
# export CFLAGS+=-fsanitize=address,undefined
export CFLAGS+=-std=c18 -pedantic -Werror
export TOY_OUTDIR = out
all: $(TOY_OUTDIR) repl
#repl builds
repl: $(TOY_OUTDIR) library
$(MAKE) -C repl
repl-static: $(TOY_OUTDIR) static
$(MAKE) -C repl
repl-release: clean $(TOY_OUTDIR) library-release
$(MAKE) -C repl release
repl-static-release: clean $(TOY_OUTDIR) static-release
$(MAKE) -C repl release
#lib builds
library: $(TOY_OUTDIR)
$(MAKE) -j8 -C source library
static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static
library-release: $(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
test-sanitized: export CFLAGS+=-fsanitize=address,undefined
test-sanitized: export LIBS+=-static-libasan
test-sanitized: export DISABLE_VALGRIND=true
test-sanitized: clean $(TOY_OUTDIR)
$(MAKE) -C test
$(TOY_OUTDIR):
mkdir $(TOY_OUTDIR)
+162
View File
@@ -0,0 +1,162 @@
#include "lib_about.h"
#include "toy_memory.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//the about keys
Toy_Literal majorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("major"));
Toy_Literal minorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("minor"));
Toy_Literal patchKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("patch"));
Toy_Literal buildKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("build"));
Toy_Literal authorKeyLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("author"));
//the about identifiers
Toy_Literal majorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("major"));
Toy_Literal minorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("minor"));
Toy_Literal patchIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("patch"));
Toy_Literal buildIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("build"));
Toy_Literal authorIdentifierLiteral = TOY_TO_IDENTIFIER_LITERAL(Toy_createRefString("author"));
//the about values
Toy_Literal majorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MAJOR);
Toy_Literal minorLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_MINOR);
Toy_Literal patchLiteral = TOY_TO_INTEGER_LITERAL(TOY_VERSION_PATCH);
Toy_Literal buildLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(TOY_VERSION_BUILD));
Toy_Literal authorLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("Kayne Ruse, KR Game Studios"));
//store as an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
Toy_freeLiteral(majorKeyLiteral);
Toy_freeLiteral(minorKeyLiteral);
Toy_freeLiteral(patchKeyLiteral);
Toy_freeLiteral(buildKeyLiteral);
Toy_freeLiteral(authorKeyLiteral);
Toy_freeLiteral(majorIdentifierLiteral);
Toy_freeLiteral(minorIdentifierLiteral);
Toy_freeLiteral(patchIdentifierLiteral);
Toy_freeLiteral(buildIdentifierLiteral);
Toy_freeLiteral(authorIdentifierLiteral);
Toy_freeLiteral(majorLiteral);
Toy_freeLiteral(minorLiteral);
Toy_freeLiteral(patchLiteral);
Toy_freeLiteral(buildLiteral);
Toy_freeLiteral(authorLiteral);
return -1;
}
//create the dictionary to load up with values
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//set each key/value pair
Toy_setLiteralDictionary(dictionary, majorKeyLiteral, majorLiteral);
Toy_setLiteralDictionary(dictionary, minorKeyLiteral, minorLiteral);
Toy_setLiteralDictionary(dictionary, patchKeyLiteral, patchLiteral);
Toy_setLiteralDictionary(dictionary, buildKeyLiteral, buildLiteral);
Toy_setLiteralDictionary(dictionary, authorKeyLiteral, authorLiteral);
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal anyType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_ANY, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, anyType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
}
//store globally
else {
//make sure the names aren't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, majorKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, minorKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, patchKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, buildKeyLiteral) ||
Toy_isDelcaredScopeVariable(interpreter->scope, authorKeyLiteral)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
Toy_freeLiteral(majorKeyLiteral);
Toy_freeLiteral(minorKeyLiteral);
Toy_freeLiteral(patchKeyLiteral);
Toy_freeLiteral(buildKeyLiteral);
Toy_freeLiteral(authorKeyLiteral);
Toy_freeLiteral(majorIdentifierLiteral);
Toy_freeLiteral(minorIdentifierLiteral);
Toy_freeLiteral(patchIdentifierLiteral);
Toy_freeLiteral(buildIdentifierLiteral);
Toy_freeLiteral(authorIdentifierLiteral);
Toy_freeLiteral(majorLiteral);
Toy_freeLiteral(minorLiteral);
Toy_freeLiteral(patchLiteral);
Toy_freeLiteral(buildLiteral);
Toy_freeLiteral(authorLiteral);
return -1;
}
Toy_Literal intType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_INTEGER, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
//major
Toy_declareScopeVariable(interpreter->scope, majorIdentifierLiteral, intType);
Toy_setScopeVariable(interpreter->scope, majorIdentifierLiteral, majorLiteral, false);
//minor
Toy_declareScopeVariable(interpreter->scope, minorIdentifierLiteral, intType);
Toy_setScopeVariable(interpreter->scope, minorIdentifierLiteral, minorLiteral, false);
//patch
Toy_declareScopeVariable(interpreter->scope, patchIdentifierLiteral, intType);
Toy_setScopeVariable(interpreter->scope, patchIdentifierLiteral, patchLiteral, false);
//build
Toy_declareScopeVariable(interpreter->scope, buildIdentifierLiteral, strType);
Toy_setScopeVariable(interpreter->scope, buildIdentifierLiteral, buildLiteral, false);
//author
Toy_declareScopeVariable(interpreter->scope, authorIdentifierLiteral, strType);
Toy_setScopeVariable(interpreter->scope, authorIdentifierLiteral, authorLiteral, false);
Toy_freeLiteral(intType);
Toy_freeLiteral(strType);
}
//cleanup
Toy_freeLiteral(majorKeyLiteral);
Toy_freeLiteral(minorKeyLiteral);
Toy_freeLiteral(patchKeyLiteral);
Toy_freeLiteral(buildKeyLiteral);
Toy_freeLiteral(authorKeyLiteral);
Toy_freeLiteral(majorIdentifierLiteral);
Toy_freeLiteral(minorIdentifierLiteral);
Toy_freeLiteral(patchIdentifierLiteral);
Toy_freeLiteral(buildIdentifierLiteral);
Toy_freeLiteral(authorIdentifierLiteral);
Toy_freeLiteral(majorLiteral);
Toy_freeLiteral(minorLiteral);
Toy_freeLiteral(patchLiteral);
Toy_freeLiteral(buildLiteral);
Toy_freeLiteral(authorLiteral);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookAbout(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+1468
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+646
View File
@@ -0,0 +1,646 @@
#include "lib_runner.h"
#include "toy_memory.h"
#include "toy_interpreter.h"
#include "repl_tools.h"
#include <stdio.h>
#include <stdlib.h>
typedef struct Toy_Runner {
Toy_Interpreter interpreter;
const unsigned char* bytecode;
size_t size;
bool dirty;
} Toy_Runner;
//Toy native functions
static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to loadScript\n");
return -1;
}
//get the file path literal with a handle
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
Toy_freeLiteral(drivePathLiteralIdn);
}
Toy_Literal filePathLiteral = Toy_getFilePathLiteral(interpreter, &drivePathLiteral);
if (TOY_IS_NULL(filePathLiteral)) {
Toy_freeLiteral(filePathLiteral);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
Toy_freeLiteral(drivePathLiteral);
//use raw types - easier
const char* filePath = Toy_toCString(TOY_AS_STRING(filePathLiteral));
int filePathLength = Toy_lengthRefString(TOY_AS_STRING(filePathLiteral));
//load and compile the bytecode
size_t fileSize = 0;
const char* source = Toy_readFile(filePath, &fileSize);
if (!source) {
interpreter->errorOutput("Failed to load source file\n");
Toy_freeLiteral(filePathLiteral);
return -1;
}
const unsigned char* bytecode = Toy_compileString(source, &fileSize);
free((void*)source);
if (!bytecode) {
interpreter->errorOutput("Failed to compile source file\n");
Toy_freeLiteral(filePathLiteral);
return -1;
}
//build the runner object
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
runner->interpreter.hooks = interpreter->hooks;
runner->interpreter.scope = NULL;
Toy_resetInterpreter(&runner->interpreter);
runner->bytecode = bytecode;
runner->size = fileSize;
runner->dirty = false;
//build the opaque object, and push it to the stack
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
//free the drive path
Toy_freeLiteral(filePathLiteral);
return 1;
}
static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to loadScriptBytecode\n");
return -1;
}
//get the argument
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
Toy_Literal drivePathLiteralIdn = drivePathLiteral;
if (TOY_IS_IDENTIFIER(drivePathLiteral) && Toy_parseIdentifierToValue(interpreter, &drivePathLiteral)) {
Toy_freeLiteral(drivePathLiteralIdn);
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n");
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
return -1;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
Toy_freeLiteral(drivePathLiteral);
//check for file extensions
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) {
interpreter->errorOutput("Bad binary file extension (expected .tb)\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
//check for break-out attempts
for (int i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength);
return -1;
}
}
//load the bytecode
size_t fileSize = 0;
unsigned char* bytecode = (unsigned char*)Toy_readFile(filePath, &fileSize);
if (!bytecode) {
interpreter->errorOutput("Failed to load bytecode file\n");
return -1;
}
//build the runner object
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
runner->interpreter.hooks = interpreter->hooks;
runner->interpreter.scope = NULL;
Toy_resetInterpreter(&runner->interpreter);
runner->bytecode = bytecode;
runner->size = fileSize;
runner->dirty = false;
//build the opaque object, and push it to the stack
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
TOY_FREE_ARRAY(char, filePath, realLength);
return 1;
}
static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
return -1;
}
//get the runner object
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
Toy_Literal runnerIdn = runnerLiteral;
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
Toy_freeLiteral(runnerIdn);
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
return -1;
}
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
//run
if (runner->dirty) {
interpreter->errorOutput("Can't re-run a dirty script (try resetting it first)\n");
Toy_freeLiteral(runnerLiteral);
return -1;
}
unsigned char* bytecodeCopy = TOY_ALLOCATE(unsigned char, runner->size);
memcpy(bytecodeCopy, runner->bytecode, runner->size); //need a COPY of the bytecode, because the interpreter eats it
Toy_runInterpreter(&runner->interpreter, bytecodeCopy, runner->size);
runner->dirty = true;
//cleanup
Toy_freeLiteral(runnerLiteral);
return 0;
}
static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n");
return -1;
}
//get the runner object
Toy_Literal varName = Toy_popLiteralArray(arguments);
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
Toy_Literal varNameIdn = varName;
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
Toy_freeLiteral(varNameIdn);
}
Toy_Literal runnerIdn = runnerLiteral;
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
Toy_freeLiteral(runnerIdn);
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
return -1;
}
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
//dirty check
if (!runner->dirty) {
interpreter->errorOutput("Can't access variable from a non-dirty script (try running it first)\n");
Toy_freeLiteral(runnerLiteral);
return -1;
}
//get the desired variable
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
Toy_Literal result = TOY_TO_NULL_LITERAL;
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &result);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteral(result);
Toy_freeLiteral(varIdn);
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
return 1;
}
static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count < 2) {
interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n");
return -1;
}
//get the rest args
Toy_LiteralArray tmp;
Toy_initLiteralArray(&tmp);
while (arguments->count > 2) {
Toy_Literal lit = Toy_popLiteralArray(arguments);
Toy_pushLiteralArray(&tmp, lit);
Toy_freeLiteral(lit);
}
Toy_LiteralArray rest;
Toy_initLiteralArray(&rest);
while (tmp.count) { //correct the order of the rest args
Toy_Literal lit = Toy_popLiteralArray(&tmp);
Toy_pushLiteralArray(&rest, lit);
Toy_freeLiteral(lit);
}
Toy_freeLiteralArray(&tmp);
//get the runner object
Toy_Literal varName = Toy_popLiteralArray(arguments);
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
Toy_Literal varNameIdn = varName;
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
Toy_freeLiteral(varNameIdn);
}
Toy_Literal runnerIdn = runnerLiteral;
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
Toy_freeLiteral(runnerIdn);
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
return -1;
}
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
//dirty check
if (!runner->dirty) {
interpreter->errorOutput("Can't access fn from a non-dirty script (try running it first)\n");
Toy_freeLiteral(runnerLiteral);
Toy_freeLiteralArray(&rest);
return -1;
}
//get the desired variable
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
Toy_Literal fn = TOY_TO_NULL_LITERAL;
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &fn);
if (!TOY_IS_FUNCTION(fn)) {
interpreter->errorOutput("Can't run a non-function literal\n");
Toy_freeLiteral(fn);
Toy_freeLiteral(varIdn);
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
Toy_freeLiteralArray(&rest);
}
//call
Toy_LiteralArray resultArray;
Toy_initLiteralArray(&resultArray);
Toy_callLiteralFn(interpreter, fn, &rest, &resultArray);
Toy_Literal result = TOY_TO_NULL_LITERAL;
if (resultArray.count > 0) {
result = Toy_popLiteralArray(&resultArray);
}
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteralArray(&resultArray);
Toy_freeLiteral(result);
Toy_freeLiteral(fn);
Toy_freeLiteral(varIdn);
Toy_freeLiteral(varName);
Toy_freeLiteral(runnerLiteral);
Toy_freeLiteralArray(&rest);
return 1;
}
static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _resetScript\n");
return -1;
}
//get the runner object
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
Toy_Literal runnerIdn = runnerLiteral;
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
Toy_freeLiteral(runnerIdn);
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
return -1;
}
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
//reset
if (!runner->dirty) {
interpreter->errorOutput("Can't reset a non-dirty script (try running it first)\n");
Toy_freeLiteral(runnerLiteral);
return -1;
}
Toy_resetInterpreter(&runner->interpreter);
runner->dirty = false;
Toy_freeLiteral(runnerLiteral);
return 0;
}
static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _freeScript\n");
return -1;
}
//get the runner object
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
Toy_Literal runnerIdn = runnerLiteral;
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
Toy_freeLiteral(runnerIdn);
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _freeScript\n");
return -1;
}
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
//clear out the runner object
runner->interpreter.hooks = NULL;
Toy_freeInterpreter(&runner->interpreter);
TOY_FREE_ARRAY(unsigned char, runner->bytecode, runner->size);
TOY_FREE(Toy_Runner, runner);
Toy_freeLiteral(runnerLiteral);
return 0;
}
static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
return -1;
}
//get the runner object
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
Toy_Literal runnerIdn = runnerLiteral;
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
Toy_freeLiteral(runnerIdn);
}
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
return -1;
}
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
//run
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(runner->dirty);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteral(result);
Toy_freeLiteral(runnerLiteral);
return 0;
}
//call the hook
typedef struct Natives {
const char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"loadScript", nativeLoadScript},
{"loadScriptBytecode", nativeLoadScriptBytecode},
{"_runScript", nativeRunScript},
{"_getScriptVar", nativeGetScriptVar},
{"_callScriptFn", nativeCallScriptFn},
{"_resetScript", nativeResetScript},
{"_freeScript", nativeFreeScript},
{"_checkScriptDirty", nativeCheckScriptDirty},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
//file system API
static Toy_LiteralDictionary Toy_driveDictionary;
void Toy_initDriveDictionary() {
Toy_initLiteralDictionary(&Toy_driveDictionary);
}
void Toy_freeDriveDictionary() {
Toy_freeLiteralDictionary(&Toy_driveDictionary);
}
Toy_LiteralDictionary* Toy_getDriveDictionary() {
return &Toy_driveDictionary;
}
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral) {
//check argument types
if (!TOY_IS_STRING(*drivePathLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(*drivePathLiteral));
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
size_t driveLength = 0;
while (Toy_toCString(drivePath)[driveLength] != ':') {
if (driveLength >= Toy_lengthRefString(drivePath)) {
interpreter->errorOutput("Incorrect drive path format given to Toy_getFilePathLiteral\n");
return TOY_TO_NULL_LITERAL;
}
driveLength++;
}
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
//get the real drive file path
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
if (!TOY_IS_STRING(realDriveLiteral)) {
interpreter->errorOutput("Incorrect literal type found for drive: ");
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
interpreter->errorOutput("\n");
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
return TOY_TO_NULL_LITERAL;
}
//get the final real file path (concat) TODO: move this concat to refstring library
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
//clean up the drivepath stuff
Toy_deleteRefString(realDrive);
Toy_freeLiteral(realDriveLiteral);
Toy_freeLiteral(driveLiteral);
Toy_deleteRefString(path);
Toy_deleteRefString(drivePath);
//check for break-out attempts
for (int i = 0; i < realLength - 1; i++) {
if (filePath[i] == '.' && filePath[i + 1] == '.') {
interpreter->errorOutput("Parent directory access not allowed\n");
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return TOY_TO_NULL_LITERAL;
}
}
Toy_Literal result = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(filePath, realLength));
TOY_FREE_ARRAY(char, filePath, realLength + 1);
return result;
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
//file system API - these need to be set by the host
void Toy_initDriveDictionary();
void Toy_freeDriveDictionary();
Toy_LiteralDictionary* Toy_getDriveDictionary();
#define TOY_OPAQUE_TAG_RUNNER 100
//file system API - for use with other libs
Toy_Literal Toy_getFilePathLiteral(Toy_Interpreter* interpreter, Toy_Literal* drivePathLiteral);
+30 -30
View File
@@ -1,10 +1,11 @@
#include "lib_standard.h"
#include "memory.h"
#include "toy_memory.h"
#include <time.h>
#include <sys/time.h>
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to clock\n");
@@ -18,13 +19,13 @@ static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
//push to the stack
int len = strlen(timestr) - 1; //-1 for the newline
Literal timeLiteral = TO_STRING_LITERAL(copyString(timestr, len), len);
Toy_Literal timeLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(timestr, len));
//push to the stack
pushLiteralArray(&interpreter->stack, timeLiteral);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
//cleanup
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return 1;
}
@@ -32,10 +33,10 @@ static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
//call the hook
typedef struct Natives {
char* name;
NativeFn fn;
Toy_NativeFn fn;
} Natives;
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"clock", nativeClock},
@@ -43,51 +44,50 @@ int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
};
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(alias);
return false;
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
func.type = LITERAL_FUNCTION_NATIVE;
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
setLiteralDictionary(dictionary, name, func);
Toy_setLiteralDictionary(dictionary, name, func);
freeLiteral(name);
freeLiteral(func);
Toy_freeLiteral(name);
Toy_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);
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
declareScopeVariable(interpreter->scope, alias, type);
setScopeVariable(interpreter->scope, alias, dict, false);
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
freeLiteral(dict);
freeLiteral(type);
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
+2 -2
View File
@@ -1,6 +1,6 @@
#pragma once
#include "interpreter.h"
#include "toy_interpreter.h"
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+136 -136
View File
@@ -1,12 +1,13 @@
#include "lib_timer.h"
#include "toy_common.h"
#include "memory.h"
#include "toy_memory.h"
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
//normallize
if (x->tv_usec > 999999) {
x->tv_sec += x->tv_usec / 1000000;
@@ -33,7 +34,7 @@ int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *
//god damn it
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
struct timeval* d = ALLOCATE(struct timeval, 1);
struct timeval* d = TOY_ALLOCATE(struct timeval, 1);
//I gave up, copied from SO
timeval_subtract(d, rhs, lhs);
@@ -42,7 +43,7 @@ static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
}
//callbacks
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeStartTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
@@ -50,19 +51,19 @@ static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
}
//get the timeinfo from C
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
struct timeval* timeinfo = TOY_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);
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return 1;
}
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeStopTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
@@ -74,34 +75,34 @@ static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
gettimeofday(&timerStop, NULL);
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
struct timeval* timerStart = TOY_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);
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(diffLiteral);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(diffLiteral);
return 1;
}
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeCreateTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
@@ -109,50 +110,50 @@ static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments)
}
//get the args
Literal microsecondLiteral = popLiteralArray(arguments);
Literal secondLiteral = popLiteralArray(arguments);
Toy_Literal microsecondLiteral = Toy_popLiteralArray(arguments);
Toy_Literal secondLiteral = Toy_popLiteralArray(arguments);
Literal secondLiteralIdn = secondLiteral;
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
freeLiteral(secondLiteralIdn);
Toy_Literal secondLiteralIdn = secondLiteral;
if (TOY_IS_IDENTIFIER(secondLiteral) && Toy_parseIdentifierToValue(interpreter, &secondLiteral)) {
Toy_freeLiteral(secondLiteralIdn);
}
Literal microsecondLiteralIdn = microsecondLiteral;
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, &microsecondLiteral)) {
freeLiteral(microsecondLiteralIdn);
Toy_Literal microsecondLiteralIdn = microsecondLiteral;
if (TOY_IS_IDENTIFIER(microsecondLiteral) && Toy_parseIdentifierToValue(interpreter, &microsecondLiteral)) {
Toy_freeLiteral(microsecondLiteralIdn);
}
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
if (!TOY_IS_INTEGER(secondLiteral) || !TOY_IS_INTEGER(microsecondLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return -1;
}
if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) {
if (TOY_AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || TOY_AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (TOY_AS_INTEGER(secondLiteral) != 0 && TOY_AS_INTEGER(microsecondLiteral) < 0) ) {
interpreter->errorOutput("Microseconds out of range in createTimer\n");
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
Toy_freeLiteral(secondLiteral);
Toy_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);
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
timeinfo->tv_sec = TOY_AS_INTEGER(secondLiteral);
timeinfo->tv_usec = TOY_AS_INTEGER(microsecondLiteral);
//wrap in an opaque literal for Toy
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
pushLiteralArray(&interpreter->stack, timeLiteral);
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
freeLiteral(timeLiteral);
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return 1;
}
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeGetTimerSeconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
@@ -160,33 +161,33 @@ static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* argumen
}
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the result literal
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
pushLiteralArray(&interpreter->stack, result);
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_sec);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(result);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(result);
return 1;
}
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeGetTimerMicroseconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
@@ -194,33 +195,33 @@ static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* ar
}
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the result literal
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
pushLiteralArray(&interpreter->stack, result);
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_usec);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(result);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(result);
return 1;
}
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeCompareTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
@@ -228,43 +229,43 @@ static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments)
}
//unwrap the opaque literals
Literal rhsLiteral = popLiteralArray(arguments);
Literal lhsLiteral = popLiteralArray(arguments);
Toy_Literal rhsLiteral = Toy_popLiteralArray(arguments);
Toy_Literal lhsLiteral = Toy_popLiteralArray(arguments);
Literal lhsLiteralIdn = lhsLiteral;
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
freeLiteral(lhsLiteralIdn);
Toy_Literal lhsLiteralIdn = lhsLiteral;
if (TOY_IS_IDENTIFIER(lhsLiteral) && Toy_parseIdentifierToValue(interpreter, &lhsLiteral)) {
Toy_freeLiteral(lhsLiteralIdn);
}
Literal rhsLiteralIdn = rhsLiteral;
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
freeLiteral(rhsLiteralIdn);
Toy_Literal rhsLiteralIdn = rhsLiteral;
if (TOY_IS_IDENTIFIER(rhsLiteral) && Toy_parseIdentifierToValue(interpreter, &rhsLiteral)) {
Toy_freeLiteral(rhsLiteralIdn);
}
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
if (!TOY_IS_OPAQUE(lhsLiteral) || !TOY_IS_OPAQUE(rhsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
freeLiteral(lhsLiteral);
freeLiteral(rhsLiteral);
Toy_freeLiteral(lhsLiteral);
Toy_freeLiteral(rhsLiteral);
return -1;
}
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
struct timeval* lhsTimer = TOY_AS_OPAQUE(lhsLiteral);
struct timeval* rhsTimer = TOY_AS_OPAQUE(rhsLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(lhsTimer, rhsTimer);
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
pushLiteralArray(&interpreter->stack, diffLiteral);
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
freeLiteral(lhsLiteral);
freeLiteral(rhsLiteral);
freeLiteral(diffLiteral);
Toy_freeLiteral(lhsLiteral);
Toy_freeLiteral(rhsLiteral);
Toy_freeLiteral(diffLiteral);
return 1;
}
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeTimerToString(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
@@ -272,44 +273,44 @@ static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments
}
//unwrap in an opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the string literal
Literal resultLiteral = TO_NULL_LITERAL;
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
char buffer[128];
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
}
else { //normal case
char buffer[128];
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
}
pushLiteralArray(&interpreter->stack, resultLiteral);
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(resultLiteral);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
static int nativeDestroyTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
@@ -317,24 +318,24 @@ static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments)
}
//unwrap in an opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
FREE(struct timeval, timer);
TOY_FREE(struct timeval, timer);
freeLiteral(timeLiteral);
Toy_freeLiteral(timeLiteral);
return 0;
}
@@ -342,10 +343,10 @@ static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments)
//call the hook
typedef struct Natives {
char* name;
NativeFn fn;
Toy_NativeFn fn;
} Natives;
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"startTimer", nativeStartTimer},
@@ -360,51 +361,50 @@ int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
};
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(alias);
return false;
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
func.type = LITERAL_FUNCTION_NATIVE;
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
setLiteralDictionary(dictionary, name, func);
Toy_setLiteralDictionary(dictionary, name, func);
freeLiteral(name);
freeLiteral(func);
Toy_freeLiteral(name);
Toy_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);
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
declareScopeVariable(interpreter->scope, alias, type);
setScopeVariable(interpreter->scope, alias, dict, false);
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
freeLiteral(dict);
freeLiteral(type);
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
+2 -2
View File
@@ -1,6 +1,6 @@
#pragma once
#include "interpreter.h"
#include "toy_interpreter.h"
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+3
View File
@@ -18,6 +18,9 @@ else
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -Wl,-rpath,. -L$(realpath $(shell pwd)/../$(TOY_OUTDIR)) $(LIBS)
endif
release: all
strip $(OUT)
$(OBJ): | $(ODIR)
$(ODIR):
+91 -49
View File
@@ -1,13 +1,16 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_compound.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "lib_runner.h"
#include "console_colors.h"
#include "toy_console_colors.h"
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
#include <stdio.h>
#include <stdlib.h>
@@ -21,16 +24,23 @@ void repl() {
char input[size];
memset(input, 0, size);
Interpreter interpreter; //persist the interpreter for the scopes
initInterpreter(&interpreter);
Toy_Interpreter interpreter; //persist the interpreter for the scopes
Toy_initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
for(;;) {
printf("> ");
fgets(input, size, stdin);
//handle EOF for exits
if (!fgets(input, size, stdin)) {
break;
}
//escape the repl (length of 5 to accomodate the newline)
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
@@ -38,104 +48,136 @@ void repl() {
}
//setup this iteration
Lexer lexer;
Parser parser;
Compiler compiler;
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
initLexer(&lexer, input);
initParser(&parser, &lexer);
initCompiler(&compiler);
Toy_initLexer(&lexer, input);
Toy_private_setComments(&lexer, false); //BUGFIX: disable comments here
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//run this iteration
ASTNode* node = scanParser(&parser);
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and restart
if (node->type == AST_NODEERROR) {
printf(ERROR "error node detected\n" RESET);
if (node->type == TOY_AST_NODE_ERROR) {
if (Toy_commandLine.verbose) {
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
}
error = true;
freeNode(node);
Toy_freeASTNode(node);
break;
}
writeCompiler(&compiler, node);
freeNode(node);
node = scanParser(&parser);
Toy_writeCompiler(&compiler, node);
Toy_freeASTNode(node);
node = Toy_scanParser(&parser);
}
if (!error) {
//get the bytecode dump
int size = 0;
unsigned char* tb = collateCompiler(&compiler, &size);
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
//run the bytecode
runInterpreter(&interpreter, tb, size);
Toy_runInterpreter(&interpreter, tb, size);
}
//clean up this iteration
freeCompiler(&compiler);
freeParser(&parser);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
error = false;
}
freeInterpreter(&interpreter);
Toy_freeInterpreter(&interpreter);
}
//entry point
int main(int argc, const char* argv[]) {
initCommand(argc, argv);
Toy_initCommandLine(argc, argv);
//command specific actions
if (command.error) {
usageCommand(argc, argv);
//lib setup (hacky - only really for this program)
Toy_initDriveDictionary();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//command line specific actions
if (Toy_commandLine.error) {
Toy_usageCommandLine(argc, argv);
return 0;
}
if (command.help) {
helpCommand(argc, argv);
if (Toy_commandLine.help) {
Toy_helpCommandLine(argc, argv);
return 0;
}
if (command.version) {
copyrightCommand(argc, argv);
if (Toy_commandLine.version) {
Toy_copyrightCommandLine(argc, argv);
return 0;
}
//TODO: remove this when the interpreter meets the specification
if (command.verbose) {
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
//version
if (Toy_commandLine.verbose) {
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d, built '%s'\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
}
//run source file
if (command.sourcefile) {
runSourceFile(command.sourcefile);
if (Toy_commandLine.sourcefile) {
Toy_runSourceFile(Toy_commandLine.sourcefile);
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
//run from stdin
if (command.source) {
runSource(command.source);
if (Toy_commandLine.source) {
Toy_runSource(Toy_commandLine.source);
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
//compile source file
if (command.compilefile && command.outfile) {
if (Toy_commandLine.compilefile && Toy_commandLine.outfile) {
size_t size = 0;
char* source = readFile(command.compilefile, &size);
unsigned char* tb = compileString(source, &size);
const char* source = Toy_readFile(Toy_commandLine.compilefile, &size);
if (!source) {
return 1;
}
const unsigned char* tb = Toy_compileString(source, &size);
if (!tb) {
return 1;
}
writeFile(command.outfile, tb, size);
Toy_writeFile(Toy_commandLine.outfile, tb, size);
return 0;
}
//run binary
if (command.binaryfile) {
runBinaryFile(command.binaryfile);
if (Toy_commandLine.binaryfile) {
Toy_runBinaryFile(Toy_commandLine.binaryfile);
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
repl();
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
+62 -52
View File
@@ -1,24 +1,27 @@
#include "repl_tools.h"
#include "lib_about.h"
#include "lib_compound.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "lib_runner.h"
#include "console_colors.h"
#include "toy_console_colors.h"
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
#include <stdio.h>
#include <stdlib.h>
//IO functions
char* readFile(char* path, size_t* fileSize) {
const char* Toy_readFile(const 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);
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
return NULL;
}
fseek(file, 0L, SEEK_END);
@@ -28,8 +31,8 @@ char* readFile(char* path, size_t* fileSize) {
char* buffer = (char*)malloc(*fileSize + 1);
if (buffer == NULL) {
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
exit(-1);
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
return NULL;
}
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
@@ -37,8 +40,8 @@ char* readFile(char* path, size_t* fileSize) {
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);
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
return NULL;
}
fclose(file);
@@ -46,98 +49,105 @@ char* readFile(char* path, size_t* fileSize) {
return buffer;
}
void writeFile(char* path, unsigned char* bytes, size_t size) {
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
FILE* file = fopen(path, "wb");
if (file == NULL) {
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
exit(-1);
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
return -1;
}
int written = fwrite(bytes, size, 1, file);
if (written != 1) {
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
exit(-1);
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
return -1;
}
fclose(file);
return 0;
}
//repl functions
unsigned char* compileString(char* source, size_t* size) {
Lexer lexer;
Parser parser;
Compiler compiler;
const unsigned char* Toy_compileString(const char* source, size_t* size) {
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
initLexer(&lexer, source);
initParser(&parser, &lexer);
initCompiler(&compiler);
Toy_initLexer(&lexer, source);
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//run the parser until the end of the source
ASTNode* node = scanParser(&parser);
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and leave
if (node->type == AST_NODEERROR) {
printf(ERROR "error node detected\n" RESET);
freeNode(node);
freeCompiler(&compiler);
freeParser(&parser);
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
return NULL;
}
writeCompiler(&compiler, node);
freeNode(node);
node = scanParser(&parser);
Toy_writeCompiler(&compiler, node);
Toy_freeASTNode(node);
node = Toy_scanParser(&parser);
}
//get the bytecode dump
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
//cleanup
freeCompiler(&compiler);
freeParser(&parser);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
//no lexer to clean up
//finally
return tb;
}
void runBinary(unsigned char* tb, size_t size) {
Interpreter interpreter;
initInterpreter(&interpreter);
void Toy_runBinary(const unsigned char* tb, size_t size) {
Toy_Interpreter interpreter;
Toy_initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter);
Toy_runInterpreter(&interpreter, tb, size);
Toy_freeInterpreter(&interpreter);
}
void runBinaryFile(char* fname) {
void Toy_runBinaryFile(const char* fname) {
size_t size = 0; //not used
unsigned char* tb = (unsigned char*)readFile(fname, &size);
const unsigned char* tb = (const unsigned char*)Toy_readFile(fname, &size);
if (!tb) {
return;
}
runBinary(tb, size);
Toy_runBinary(tb, size);
//interpreter takes ownership of the binary data
}
void runSource(char* source) {
void Toy_runSource(const char* source) {
size_t size = 0;
unsigned char* tb = compileString(source, &size);
const unsigned char* tb = Toy_compileString(source, &size);
if (!tb) {
return;
}
runBinary(tb, size);
Toy_runBinary(tb, size);
}
void runSourceFile(char* fname) {
void Toy_runSourceFile(const char* fname) {
size_t size = 0; //not used
char* source = readFile(fname, &size);
runSource(source);
const char* source = Toy_readFile(fname, &size);
if (!source) {
return;
}
Toy_runSource(source);
free((void*)source);
}
+7 -7
View File
@@ -2,13 +2,13 @@
#include "toy_common.h"
char* readFile(char* path, size_t* fileSize);
void writeFile(char* path, unsigned char* bytes, size_t size);
const char* Toy_readFile(const char* path, size_t* fileSize);
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
unsigned char* compileString(char* source, size_t* size);
const unsigned char* Toy_compileString(const 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);
void Toy_runBinary(const unsigned char* tb, size_t size);
void Toy_runBinaryFile(const char* fname);
void Toy_runSource(const char* source);
void Toy_runSourceFile(const char* fname);
+125
View File
@@ -0,0 +1,125 @@
import node;
//constants
var SPEED: int const = 10;
//variables
var parent: opaque = null;
var posX: int = 50;
var posY: int = 50;
var WIDTH: int const = 100;
var HEIGHT: int const = 100;
var xspeed: int = 0;
var yspeed: int = 0;
//accessors - variables are private, functions are public
fn getX(node: opaque) {
return posX;
}
fn getY(node: opaque) {
return posY;
}
//lifecycle functions
fn onInit(node: opaque) {
print "render.toy:onInit() called\n";
node.loadTexture("sprites:/character.png");
parent = node.getNodeParent();
}
fn onStep(node: opaque) {
posX += xspeed;
posY += yspeed;
}
fn onFree(node: opaque) {
print "render.toy:onFree() called\n";
node.freeTexture();
}
fn onDraw(node: opaque) {
// print "render.toy:onDraw() called\n";
var px = parent.callNode("getX");
var py = parent.callNode("getY");
if (px == null) {
px = 0;
}
if (py == null) {
py = 0;
}
node.drawNode(posX + px, posY + py, WIDTH, HEIGHT);
}
//event functions
fn onKeyDown(node: opaque, event: string) {
if (event == "character_up") {
yspeed -= SPEED;
return;
}
if (event == "character_down") {
yspeed += SPEED;
return;
}
if (event == "character_left") {
xspeed -= SPEED;
return;
}
if (event == "character_right") {
xspeed += SPEED;
return;
}
}
fn onKeyUp(node: opaque, event: string) {
if (event == "character_up" && yspeed < 0) {
yspeed = 0;
return;
}
if (event == "character_down" && yspeed > 0) {
yspeed = 0;
return;
}
if (event == "character_left" && xspeed < 0) {
xspeed = 0;
return;
}
if (event == "character_right" && xspeed > 0) {
xspeed = 0;
return;
}
}
fn onMouseMotion(node: opaque, x: int, y: int, xrel: int, yrel: int) {
// print "entity.toy:onMouseMotion(" + string x + ", " + string y + ", " + string xrel + ", " + string yrel + ")\n";
}
fn onMouseButtonDown(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonDown(" + string x + ", " + string y + ", " + button + ")\n";
//jump to pos
posX = x - WIDTH / 2;
posY = y - HEIGHT / 2;
}
fn onMouseButtonUp(node: opaque, x: int, y: int, button: string) {
// print "entity.toy:onMouseButtonUp(" + string x + ", " + string y + ", " + button + ")\n";
}
fn onMouseWheel(node: opaque, xrel: int, yrel: int) {
// print "entity.toy:onMouseWheel(" + string xrel + ", " + string yrel + ")\n";
}
+21
View File
@@ -0,0 +1,21 @@
//memoize the fib function
var memo: [int : int] = [:];
fn fib(n : int) {
if (n < 2) {
return n;
}
var result = memo[n];
if (result == null) {
result = fib(n-1) + fib(n-2);
memo[n] = result;
}
return result;
}
for (var i = 0; i < 40; i++) {
var res = fib(i);
print string i + ": " + string res + "\n";
}
+3 -6
View File
@@ -1,12 +1,9 @@
fn fib(n : int) {
if (n < 2) {
return n;
}
return fib(n-1) + fib(n-2);
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
for (var i = 0; i < 20; i++) {
var res = fib(i);
print string i + ": " + string res;
print string i + ": " + string res + "\n";
}
+61
View File
@@ -0,0 +1,61 @@
//constants
var WIDTH: int const = 10;
var HEIGHT: int const = 10;
//WIDTH * HEIGHT in size
var tiles: [[int]] const = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
var tileset: [int: string] const = [
0: " ",
1: " X "
];
//variables
var posX: int = 5;
var posY: int = 5;
//functions
fn draw() {
for (var j: int = 0; j < HEIGHT; j++) {
for (var i: int = 0; i < WIDTH; i++) {
//draw the player pos
if (i == posX && j == posY) {
print " O ";
continue;
}
print tileset[ tiles[i][j] ];
}
print "\n";
}
print "\n";
}
fn move(xrel: int, yrel: int) {
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1 || (xrel != 0 && yrel != 0)) {
print "too fast!\n";
return;
}
if (tiles[posX + xrel][posY + yrel] > 0) {
print "Can't move that way\n";
return;
}
posX += xrel;
posY += yrel;
draw();
}
+41 -58
View File
@@ -1,66 +1,49 @@
var size: int const = 100;
//number of iterations
var SIZE: int const = 260;
var prev = [];
for (var i = 0; i < size; i++) {
prev.push(false);
//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 + "\n";
prev.set(size - 1, true);
//run
for (var iteration = 0; iteration < SIZE -1; iteration++) {
//left
var output = (lookup[" "][prev[0]][prev[1]]);
fn calc(p, i) {
if (p[i-1] && p[i] && p[i+1]) {
return false;
//middle
for (var i = 1; i < SIZE-1; i++) {
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
}
if (p[i-1] && p[i] && !p[i+1]) {
return true;
}
//right
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
if (p[i-1] && !p[i] && p[i+1]) {
return true;
}
if (p[i-1] && !p[i] && !p[i+1]) {
return false;
}
if (!p[i-1] && p[i] && p[i+1]) {
return true;
}
if (!p[i-1] && p[i] && !p[i+1]) {
return true;
}
if (!p[i-1] && !p[i] && p[i+1]) {
return true;
}
if (!p[i-1] && !p[i] && !p[i+1]) {
return false;
}
}
for (var iteration = 0; iteration < 100; iteration++) {
var line = [false];
for (var i = 1; i < size-1; i++) {
line.push(calc(prev, i));
}
line.push(false);
var output = "";
for (var i = 0; i < line.length(); i++) {
if (line[i]) {
output += "*";
}
else {
output += " ";
}
}
print output;
prev = line;
print output + "\n";
prev = output;
}
+8 -11
View File
@@ -1,13 +1,10 @@
import timer;
var a = createTimer(1, 0);
var b = createTimer(2, 0);
var xrel: int = 0;
var yrel: int = 0;
print a.compareTimer(b).timerToString();
print b.compareTimer(a).timerToString();
var c = createTimer(0, 1);
var d = createTimer(0, 2);
print c.compareTimer(d).timerToString();
print d.compareTimer(c).timerToString();
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1) {
print "outside";
}
else {
print "inside";
}
-294
View File
@@ -1,294 +0,0 @@
#include "ast_node.h"
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>
void freeNodeCustom(ASTNode* node, bool freeSelf) {
//don't free a NULL node
if (node == NULL) {
return;
}
switch(node->type) {
case AST_NODEERROR:
//NO-OP
break;
case AST_NODELITERAL:
freeLiteral(node->atomic.literal);
break;
case AST_NODEUNARY:
freeNode(node->unary.child);
break;
case AST_NODEBINARY:
freeNode(node->binary.left);
freeNode(node->binary.right);
break;
case AST_NODEGROUPING:
freeNode(node->grouping.child);
break;
case AST_NODEBLOCK:
for (int i = 0; i < node->block.count; i++) {
freeNodeCustom(node->block.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
break;
case AST_NODECOMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeNodeCustom(node->compound.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
break;
case AST_NODEPAIR:
freeNode(node->pair.left);
freeNode(node->pair.right);
break;
case AST_NODEVAR_DECL:
freeLiteral(node->varDecl.identifier);
freeLiteral(node->varDecl.typeLiteral);
freeNode(node->varDecl.expression);
break;
case AST_NODEFN_DECL:
freeLiteral(node->fnDecl.identifier);
freeNode(node->fnDecl.arguments);
freeNode(node->fnDecl.returns);
freeNode(node->fnDecl.block);
break;
case AST_NODEFN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeNodeCustom(node->fnCollection.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case AST_NODEFN_CALL:
freeNode(node->fnCall.arguments);
break;
case AST_NODEPATH_IF:
case AST_NODEPATH_WHILE:
case AST_NODEPATH_FOR:
case AST_NODEPATH_BREAK:
case AST_NODEPATH_CONTINUE:
case AST_NODEPATH_RETURN:
freeNode(node->path.preClause);
freeNode(node->path.postClause);
freeNode(node->path.condition);
freeNode(node->path.thenPath);
freeNode(node->path.elsePath);
break;
case AST_NODEINCREMENT_PREFIX:
case AST_NODEINCREMENT_POSTFIX:
freeLiteral(node->increment.identifier);
break;
case AST_NODEIMPORT:
case AST_NODEEXPORT:
freeLiteral(node->import.identifier);
freeLiteral(node->import.alias);
break;
case AST_NODEINDEX:
case AST_NODEDOT:
freeNode(node->index.first);
freeNode(node->index.second);
freeNode(node->index.third);
break;
}
if (freeSelf) {
FREE(ASTNode, node);
}
}
void freeNode(ASTNode* node) {
freeNodeCustom(node, true);
}
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODELITERAL;
(*nodeHandle)->atomic.literal = copyLiteral(literal);
}
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODEUNARY;
(*nodeHandle)->unary.opcode = opcode;
(*nodeHandle)->unary.child = child;
}
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEBINARY;
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_NODEGROUPING;
tmp->grouping.child = *nodeHandle;
*nodeHandle = tmp;
}
void emitASTNodeBlock(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEBLOCK;
tmp->block.nodes = NULL;
tmp->block.capacity = 0;
tmp->block.count = 0;
*nodeHandle = tmp;
}
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODECOMPOUND;
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) {
//assume the node has already been allocated
node->type = AST_NODEPAIR;
node->pair.left = left;
node->pair.right = right;
}
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEVAR_DECL;
tmp->varDecl.identifier = identifier;
tmp->varDecl.typeLiteral = typeLiteral;
tmp->varDecl.expression = expression;
*nodeHandle = tmp;
}
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_DECL;
tmp->fnDecl.identifier = identifier;
tmp->fnDecl.arguments = arguments;
tmp->fnDecl.returns = returns;
tmp->fnDecl.block = block;
*nodeHandle = tmp;
}
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_CALL;
tmp->fnCall.arguments = arguments;
tmp->fnCall.argumentCount = argumentCount;
*nodeHandle = tmp;
}
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_COLLECTION;
tmp->fnCollection.nodes = NULL;
tmp->fnCollection.capacity = 0;
tmp->fnCollection.count = 0;
*nodeHandle = tmp;
}
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = type;
tmp->path.preClause = preClause;
tmp->path.postClause = postClause;
tmp->path.condition = condition;
tmp->path.thenPath = thenPath;
tmp->path.elsePath = elsePath;
*nodeHandle = tmp;
}
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINCREMENT_PREFIX;
tmp->increment.identifier = copyLiteral(identifier);
tmp->increment.increment = increment;
*nodeHandle = tmp;
}
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINCREMENT_POSTFIX;
tmp->increment.identifier = copyLiteral(identifier);
tmp->increment.increment = increment;
*nodeHandle = tmp;
}
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = mode;
tmp->import.identifier = copyLiteral(identifier);
tmp->import.alias = copyLiteral(alias);
*nodeHandle = tmp;
}
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINDEX;
tmp->index.first = first;
tmp->index.second = second;
tmp->index.third = third;
*nodeHandle = tmp;
}
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEDOT;
tmp->index.first = first;
tmp->index.second = NULL;
tmp->index.third = NULL;
*nodeHandle = tmp;
}
-175
View File
@@ -1,175 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
#include "opcodes.h"
#include "token_types.h"
//nodes are the intermediaries between parsers and compilers
typedef union _node ASTNode;
typedef enum ASTNodeType {
AST_NODEERROR,
AST_NODELITERAL, //a simple value
AST_NODEUNARY, //one child + opcode
AST_NODEBINARY, //two children, left and right + opcode
AST_NODEGROUPING, //one child
AST_NODEBLOCK, //contains a sub-node array
AST_NODECOMPOUND, //contains a sub-node array
AST_NODEPAIR, //contains a left and right
AST_NODEVAR_DECL, //contains identifier literal, typenode, expression definition
AST_NODEFN_DECL, //containd identifier literal, arguments node, returns node, block node
AST_NODEFN_COLLECTION, //parts of a function
AST_NODEFN_CALL,
AST_NODEPATH_IF, //for control flow
AST_NODEPATH_WHILE, //for control flow
AST_NODEPATH_FOR, //for control flow
AST_NODEPATH_BREAK, //for control flow
AST_NODEPATH_CONTINUE, //for control flow
AST_NODEPATH_RETURN,
AST_NODEINCREMENT_PREFIX,
AST_NODEINCREMENT_POSTFIX,
AST_NODEIMPORT,
AST_NODEEXPORT,
AST_NODEINDEX,
AST_NODEDOT,
} ASTNodeType;
typedef struct NodeLiteral {
ASTNodeType type;
Literal literal;
} NodeLiteral;
typedef struct NodeUnary {
ASTNodeType type;
Opcode opcode;
ASTNode* child;
} NodeUnary;
typedef struct NodeBinary {
ASTNodeType type;
Opcode opcode;
ASTNode* left;
ASTNode* right;
} NodeBinary;
typedef struct NodeGrouping {
ASTNodeType type;
ASTNode* child;
} NodeGrouping;
typedef struct NodeBlock {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeBlock;
typedef struct NodeCompound {
ASTNodeType type;
LiteralType literalType;
ASTNode* nodes;
int capacity;
int count;
} NodeCompound;
typedef struct NodePair {
ASTNodeType type;
ASTNode* left;
ASTNode* right;
} NodePair;
typedef struct NodeVarDecl {
ASTNodeType type;
Literal identifier;
Literal typeLiteral;
ASTNode* expression;
} NodeVarDecl;
typedef struct NodeFnDecl {
ASTNodeType type;
Literal identifier;
ASTNode* arguments;
ASTNode* returns;
ASTNode* block;
} NodeFnDecl;
typedef struct NodeFnCollection {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeFnCollection;
typedef struct NodeFnCall {
ASTNodeType type;
ASTNode* arguments;
int argumentCount;
} NodeFnCall;
typedef struct NodePath {
ASTNodeType type;
ASTNode* preClause;
ASTNode* postClause;
ASTNode* condition;
ASTNode* thenPath;
ASTNode* elsePath;
} NodePath;
typedef struct NodeIncrement {
ASTNodeType type;
Literal identifier;
int increment;
} NodeIncrement;
typedef struct NodeImport {
ASTNodeType type;
Literal identifier;
Literal alias;
} NodeImport;
typedef struct NodeIndex {
ASTNodeType type;
ASTNode* first;
ASTNode* second;
ASTNode* third;
} NodeIndex;
union _node {
ASTNodeType type;
NodeLiteral atomic;
NodeUnary unary;
NodeBinary binary;
NodeGrouping grouping;
NodeBlock block;
NodeCompound compound;
NodePair pair;
NodeVarDecl varDecl;
NodeFnDecl fnDecl;
NodeFnCollection fnCollection;
NodeFnCall fnCall;
NodePath path;
NodeIncrement increment;
NodeImport import;
NodeIndex index;
};
TOY_API void freeNode(ASTNode* node);
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
void emitASTNodeGrouping(ASTNode** nodeHandle);
void emitASTNodeBlock(ASTNode** nodeHandle);
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right);
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount);
void emitASTNodeFnCollection(ASTNode** nodeHandle);
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias);
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first);
-1345
View File
File diff suppressed because it is too large Load Diff
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#include "interpreter.h"
int _index(Interpreter* interpreter, LiteralArray* arguments);
int _set(Interpreter* interpreter, LiteralArray* arguments);
int _get(Interpreter* interpreter, LiteralArray* arguments);
int _push(Interpreter* interpreter, LiteralArray* arguments);
int _pop(Interpreter* interpreter, LiteralArray* arguments);
int _length(Interpreter* interpreter, LiteralArray* arguments);
int _clear(Interpreter* interpreter, LiteralArray* arguments);
-1164
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include "toy_common.h"
#include "opcodes.h"
#include "ast_node.h"
#include "literal_array.h"
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
typedef struct Compiler {
LiteralArray literalCache;
unsigned char* bytecode;
int capacity;
int count;
} Compiler;
TOY_API void initCompiler(Compiler* compiler);
TOY_API void writeCompiler(Compiler* compiler, ASTNode* node);
TOY_API void freeCompiler(Compiler* compiler);
//embed the header, data section, code section, function section, etc.
TOY_API unsigned char* collateCompiler(Compiler* compiler, int* size);
-30
View File
@@ -1,30 +0,0 @@
#pragma once
//NOTE: you need both font AND background for these to work
//fonts color
#define FONT_BLACK "\033[30;"
#define FONT_RED "\033[31;"
#define FONT_GREEN "\033[32;"
#define FONT_YELLOW "\033[33;"
#define FONT_BLUE "\033[34;"
#define FONT_PURPLE "\033[35;"
#define FONT_DGREEN "\033[6;"
#define FONT_WHITE "\033[7;"
#define FONT_CYAN "\x1b[36m"
//background color
#define BACK_BLACK "40m"
#define BACK_RED "41m"
#define BACK_GREEN "42m"
#define BACK_YELLOW "43m"
#define BACK_BLUE "44m"
#define BACK_PURPLE "45m"
#define BACK_DGREEN "46m"
#define BACK_WHITE "47m"
//useful
#define NOTICE FONT_GREEN BACK_BLACK
#define WARN FONT_YELLOW BACK_BLACK
#define ERROR FONT_RED BACK_BLACK
#define RESET "\033[0m"
-2543
View File
File diff suppressed because it is too large Load Diff
-57
View File
@@ -1,57 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
typedef void (*PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions
typedef struct Interpreter {
//input
unsigned char* bytecode;
int length;
int count;
int codeStart; //BUGFIX: for jumps, must be initialized to -1
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
//operation
Scope* scope;
LiteralArray stack;
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
LiteralDictionary* exportTypes;
LiteralDictionary* hooks;
//debug outputs
PrintFn printOutput;
PrintFn assertOutput;
PrintFn errorOutput;
int depth; //don't overflow
bool panic;
} Interpreter;
//native API
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
TOY_API bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* arguments, LiteralArray* returns);
TOY_API bool callFn(Interpreter* interpreter, char* name, LiteralArray* arguments, LiteralArray* returns);
//utilities for the host program
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
TOY_API void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput);
TOY_API void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
TOY_API void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
//main access
TOY_API void initInterpreter(Interpreter* interpreter); //start of program
TOY_API void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
TOY_API void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
TOY_API void freeInterpreter(Interpreter* interpreter); //end of program
-77
View File
@@ -1,77 +0,0 @@
#include "keyword_types.h"
#include "toy_common.h"
#include <string.h>
KeywordType keywordTypes[] = {
//type keywords
{TOKEN_NULL, "null"},
{TOKEN_BOOLEAN, "bool"},
{TOKEN_INTEGER, "int"},
{TOKEN_FLOAT, "float"},
{TOKEN_STRING, "string"},
{TOKEN_FUNCTION, "fn"},
{TOKEN_OPAQUE, "opaque"},
{TOKEN_ANY, "any"},
//other keywords
{TOKEN_AS, "as"},
{TOKEN_ASSERT, "assert"},
{TOKEN_BREAK, "break"},
{TOKEN_CLASS, "class"},
{TOKEN_CONST, "const"},
{TOKEN_CONTINUE, "continue"},
{TOKEN_DO, "do"},
{TOKEN_ELSE, "else"},
{TOKEN_EXPORT, "export"},
{TOKEN_FOR, "for"},
{TOKEN_FOREACH, "foreach"},
{TOKEN_IF, "if"},
{TOKEN_IMPORT, "import"},
{TOKEN_IN, "in"},
{TOKEN_OF, "of"},
{TOKEN_PRINT, "print"},
{TOKEN_RETURN, "return"},
{TOKEN_TYPE, "type"},
{TOKEN_ASTYPE, "astype"},
{TOKEN_TYPEOF, "typeof"},
{TOKEN_VAR, "var"},
{TOKEN_WHILE, "while"},
//literal values
{TOKEN_LITERAL_TRUE, "true"},
{TOKEN_LITERAL_FALSE, "false"},
//meta tokens
{TOKEN_PASS, NULL},
{TOKEN_ERROR, NULL},
{TOKEN_EOF, NULL},
};
char* findKeywordByType(TokenType type) {
if (type == TOKEN_EOF) {
return "EOF";
}
for(int i = 0; keywordTypes[i].keyword; i++) {
if (keywordTypes[i].type == type) {
return keywordTypes[i].keyword;
}
}
return NULL;
}
TokenType findTypeByKeyword(const char* keyword) {
const int length = strlen(keyword);
for (int i = 0; keywordTypes[i].keyword; i++) {
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
return keywordTypes[i].type;
}
}
return TOKEN_EOF;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "token_types.h"
typedef struct {
TokenType type;
char* keyword;
} KeywordType;
extern KeywordType keywordTypes[];
char* findKeywordByType(TokenType type);
TokenType findTypeByKeyword(const char* keyword);
-348
View File
@@ -1,348 +0,0 @@
#include "lexer.h"
#include "console_colors.h"
#include "keyword_types.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//static generic utility functions
static void cleanLexer(Lexer* lexer) {
lexer->source = NULL;
lexer->start = 0;
lexer->current = 0;
lexer->line = 1;
}
static bool isAtEnd(Lexer* lexer) {
return lexer->source[lexer->current] == '\0';
}
static char peek(Lexer* lexer) {
return lexer->source[lexer->current];
}
static char peekNext(Lexer* lexer) {
if (isAtEnd(lexer)) return '\0';
return lexer->source[lexer->current + 1];
}
static char advance(Lexer* lexer) {
if (isAtEnd(lexer)) {
return '\0';
}
//new line
if (lexer->source[lexer->current] == '\n') {
lexer->line++;
}
lexer->current++;
return lexer->source[lexer->current - 1];
}
static void eatWhitespace(Lexer* lexer) {
const char c = peek(lexer);
switch(c) {
case ' ':
case '\r':
case '\n':
case '\t':
advance(lexer);
break;
//comments
case '/':
//eat the line
if (peekNext(lexer) == '/') {
while (advance(lexer) != '\n' && !isAtEnd(lexer));
break;
}
//eat the block
if (peekNext(lexer) == '*') {
advance(lexer);
advance(lexer);
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
advance(lexer);
advance(lexer);
break;
}
return;
default:
return;
}
//tail recursion
eatWhitespace(lexer);
}
static bool isDigit(Lexer* lexer) {
return peek(lexer) >= '0' && peek(lexer) <= '9';
}
static bool isAlpha(Lexer* lexer) {
return
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
peek(lexer) == '_'
;
}
static bool match(Lexer* lexer, char c) {
if (peek(lexer) == c) {
advance(lexer);
return true;
}
return false;
}
//token generators
static Token makeErrorToken(Lexer* lexer, char* msg) {
Token token;
token.type = TOKEN_ERROR;
token.lexeme = msg;
token.length = strlen(msg);
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("err:");
printToken(&token);
}
#endif
return token;
}
static Token makeToken(Lexer* lexer, TokenType type) {
Token token;
token.type = type;
token.length = lexer->current - lexer->start;
token.lexeme = &lexer->source[lexer->current - token.length];
token.line = lexer->line;
#ifndef TOY_EXPORT
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
if (command.verbose) {
printf("tok:");
printToken(&token);
}
#endif
return token;
}
static Token makeIntegerOrFloat(Lexer* lexer) {
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
while(isDigit(lexer)) advance(lexer);
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
type = TOKEN_LITERAL_FLOAT;
advance(lexer);
while(isDigit(lexer)) advance(lexer);
}
Token token;
token.type = type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
if (type == TOKEN_LITERAL_INTEGER) {
printf("int:");
} else {
printf("flt:");
}
printToken(&token);
}
#endif
return token;
}
static Token makeString(Lexer* lexer, char terminator) {
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
advance(lexer);
}
advance(lexer); //eat terminator
if (isAtEnd(lexer)) {
return makeErrorToken(lexer, "Unterminated string");
}
Token token;
token.type = TOKEN_LITERAL_STRING;
token.lexeme = &lexer->source[lexer->start + 1];
token.length = lexer->current - lexer->start - 2;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("str:");
printToken(&token);
}
#endif
return token;
}
static Token makeKeywordOrIdentifier(Lexer* lexer) {
advance(lexer); //first letter can only be alpha
while(isDigit(lexer) || isAlpha(lexer)) {
advance(lexer);
}
//scan for a keyword
for (int i = 0; keywordTypes[i].keyword; i++) {
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
Token token;
token.type = keywordTypes[i].type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("kwd:");
printToken(&token);
}
#endif
return token;
}
}
//return an identifier
Token token;
token.type = TOKEN_IDENTIFIER;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("idf:");
printToken(&token);
}
#endif
return token;
}
//exposed functions
void initLexer(Lexer* lexer, char* source) {
cleanLexer(lexer);
lexer->source = source;
}
Token scanLexer(Lexer* lexer) {
eatWhitespace(lexer);
lexer->start = lexer->current;
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
char c = advance(lexer);
switch(c) {
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
case '[': return makeToken(lexer, TOKEN_BRACKET_LEFT);
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
case '&': //TOKEN_AND not used
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
} else {
return makeToken(lexer, TOKEN_AND);
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
case ':': return makeToken(lexer, TOKEN_COLON);
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
case ',': return makeToken(lexer, TOKEN_COMMA);
case '.':
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
advance(lexer);
advance(lexer);
return makeToken(lexer, TOKEN_REST);
}
return makeToken(lexer, TOKEN_DOT);
case '"':
return makeString(lexer, c);
//TODO: possibly support interpolated strings
default: {
char buffer[128];
snprintf(buffer, 128, "Unexpected token: %c", c);
return makeErrorToken(lexer, buffer);
}
}
}
static void trim(char** s, int* l) { //all this to remove a newline?
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
}
//for debugging
void printToken(Token* token) {
if (token->type == TOKEN_ERROR) {
printf(ERROR "Error\t%d\t%.*s\n" RESET, token->line, token->length, token->lexeme);
return;
}
printf("\t%d\t%d\t", token->type, token->line);
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
printf("%.*s\t", token->length, token->lexeme);
} else {
char* keyword = findKeywordByType(token->type);
if (keyword != NULL) {
printf("%s", keyword);
} else {
char* str = token->lexeme;
int length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
}
}
printf("\n");
}
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include "toy_common.h"
#include "token_types.h"
//lexers are bound to a string of code, and return a single token every time scan is called
typedef struct {
char* source;
int start; //start of the token
int current; //current position of the lexer
int line; //track this for error handling
} Lexer;
//tokens are intermediaries between lexers and parsers
typedef struct {
TokenType type;
char* lexeme;
int length;
int line;
} Token;
TOY_API void initLexer(Lexer* lexer, char* source);
Token scanLexer(Lexer* lexer);
//for debugging
void printToken(Token* token);
-703
View File
@@ -1,703 +0,0 @@
#include "literal.h"
#include "memory.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
#include "console_colors.h"
#include <stdio.h>
//hash util functions
static unsigned int hashString(const char* string, int length) {
unsigned int hash = 2166136261u;
for (int i = 0; i < length; i++) {
hash *= string[i];
hash ^= 16777619;
}
return hash;
}
static unsigned int hash(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) {
if (IS_STRING(literal)) {
FREE_ARRAY(char, AS_STRING(literal), literal.as.string.length + 1);
return;
}
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;
}
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_IDENTIFIER(literal)) {
FREE_ARRAY(char, AS_IDENTIFIER(literal), literal.as.identifier.length + 1);
return;
}
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(char* str, int length) {
return ((Literal){LITERAL_STRING, { .string.ptr = (char*)str, .string.length = length }});
}
Literal _toIdentifierLiteral(char* str, int length) {
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = (char*)str, .identifier.length = length, .identifier.hash = hashString(str, length) }});
}
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(copyString(AS_STRING(original), original.as.string.length), original.as.string.length);
}
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(copyString(AS_IDENTIFIER(original), original.as.identifier.length), original.as.identifier.length);
}
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;
}
}
char* copyString(char* original, int length) {
//make a local copy of the char array
char* buffer = ALLOCATE(char, length + 1);
strncpy(buffer, original, length);
buffer[length] = '\0';
return buffer;
}
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:
if (lhs.as.string.length != rhs.as.string.length) {
return false;
}
return !strncmp(AS_STRING(lhs), AS_STRING(rhs), lhs.as.string.length);
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) && lhs.as.identifier.length != rhs.as.identifier.length) {
return false;
}
return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), lhs.as.identifier.length);
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 hash((unsigned int)AS_INTEGER(lit));
case LITERAL_FLOAT:
return hash(*(unsigned int*)(&AS_FLOAT(lit)));
case LITERAL_STRING:
return hashString(AS_STRING(lit), strlen(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 hash(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 hash(res);
}
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
return 0; //TODO: find a way to hash these properly
case LITERAL_IDENTIFIER:
return HASH_I(lit); //pre-computed
case LITERAL_TYPE:
return AS_TYPE(lit).typeOf; //nothing else I can do
case LITERAL_OPAQUE:
case LITERAL_ANY:
return -1;
default:
//should never bee seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type);
return 0;
}
}
//utils
static void stdoutWrapper(const char* output) {
printf("%s", output);
}
//buffer the prints
static char* globalPrintBuffer = NULL;
static size_t globalPrintCapacity = 0;
static size_t globalPrintCount = 0;
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
static char quotes = 0; //set to 0 to not show string quotes
static void printToBuffer(const char* str) {
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
int oldCapacity = globalPrintCapacity;
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
globalPrintCount += strlen(str);
}
//exposed functions
void printLiteral(Literal literal) {
printLiteralCustom(literal, stdoutWrapper);
}
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
switch(literal.type) {
case LITERAL_NULL:
printFn("null");
break;
case LITERAL_BOOLEAN:
printFn(AS_BOOLEAN(literal) ? "true" : "false");
break;
case LITERAL_INTEGER: {
char buffer[256];
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
printFn(buffer);
}
break;
case LITERAL_FLOAT: {
char buffer[256];
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
printFn(buffer);
}
break;
case LITERAL_STRING: {
char buffer[MAX_STRING_LENGTH];
if (!quotes) {
snprintf(buffer, MAX_STRING_LENGTH, "%.*s", (int)strlen(AS_STRING(literal)), AS_STRING(literal));
}
else {
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)strlen(AS_STRING(literal)), AS_STRING(literal), quotes);
}
printFn(buffer);
}
break;
case LITERAL_ARRAY: {
LiteralArray* ptr = AS_ARRAY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
printToBuffer("[");
for (int i = 0; i < ptr->count; i++) {
quotes = '"';
printLiteralCustom(ptr->literals[i], printToBuffer);
if (i + 1 < ptr->count) {
printToBuffer(",");
}
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case LITERAL_DICTIONARY: {
LiteralDictionary* ptr = AS_DICTIONARY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
int delimCount = 0;
printToBuffer("[");
for (int i = 0; i < ptr->capacity; i++) {
if (IS_NULL(ptr->entries[i].key)) {
continue;
}
if (delimCount++ > 0) {
printToBuffer(",");
}
quotes = '"';
printLiteralCustom(ptr->entries[i].key, printToBuffer);
printToBuffer(":");
quotes = '"';
printLiteralCustom(ptr->entries[i].value, printToBuffer);
}
//empty dicts MUST have a ":" printed
if (ptr->count == 0) {
printToBuffer(":");
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
//TODO: functions
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
printFn("(function)");
break;
case LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", (int)strlen( AS_IDENTIFIER(literal) ), 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
@@ -1,128 +0,0 @@
#pragma once
#include "toy_common.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 {
char* ptr;
int length;
} string;
void* array;
void* dictionary;
struct {
void* bytecode;
void* scope;
int length;
} function;
struct { //for variable names
char* ptr;
int length;
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;
} 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, l) _toStringLiteral(value, l)
#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, l) _toIdentifierLiteral(value, l)
#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(char* str, int length);
TOY_API Literal _toIdentifierLiteral(char* str, int length);
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
//utils
TOY_API Literal copyLiteral(Literal original);
TOY_API char* copyString(char* original, int length);
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*));
-100
View File
@@ -1,100 +0,0 @@
#include "literal_array.h"
#include "memory.h"
#include <stdio.h>
#include <string.h>
//exposed functions
void initLiteralArray(LiteralArray* array) {
array->capacity = 0;
array->count = 0;
array->literals = NULL;
}
void freeLiteralArray(LiteralArray* array) {
//clean up memory
for(int i = 0; i < array->count; i++) {
freeLiteral(array->literals[i]);
}
FREE_ARRAY(Literal, array->literals, array->capacity);
initLiteralArray(array);
}
int pushLiteralArray(LiteralArray* array, Literal literal) {
if (array->capacity < array->count + 1) {
int oldCapacity = array->capacity;
array->capacity = GROW_CAPACITY(oldCapacity);
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
}
array->literals[array->count] = copyLiteral(literal);
return array->count++;
}
Literal popLiteralArray(LiteralArray* array) {
if (array->count <= 0) {
return TO_NULL_LITERAL;
}
//get the return
Literal ret = array->literals[array->count-1];
//null the existing data
array->literals[array->count-1] = TO_NULL_LITERAL;
array->count--;
return ret;
}
//find a literal in the array that matches the "literal" argument
int findLiteralIndex(LiteralArray* array, Literal literal) {
for (int i = 0; i < array->count; i++) {
//not the same type
if (array->literals[i].type != literal.type) {
continue;
}
//types match?
if (literalsAreEqual(array->literals[i], literal)) {
return i;
}
}
return -1;
}
bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
if (!IS_INTEGER(index)) {
return false;
}
int idx = AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return false;
}
//TODO: implicit push when referencing one-past-the-end?
freeLiteral(array->literals[idx]);
array->literals[idx] = copyLiteral(value);
return true;
}
Literal getLiteralArray(LiteralArray* array, Literal index) {
if (!IS_INTEGER(index)) {
return TO_NULL_LITERAL;
}
int idx = AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return TO_NULL_LITERAL;
}
return copyLiteral(array->literals[idx]);
}
-20
View File
@@ -1,20 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
typedef struct LiteralArray {
Literal* literals;
int capacity;
int count;
} LiteralArray;
TOY_API void initLiteralArray(LiteralArray* array);
TOY_API void freeLiteralArray(LiteralArray* array);
TOY_API int pushLiteralArray(LiteralArray* array, Literal literal);
TOY_API Literal popLiteralArray(LiteralArray* array);
TOY_API bool setLiteralArray(LiteralArray* array, Literal index, Literal value);
TOY_API Literal getLiteralArray(LiteralArray* array, Literal index);
int findLiteralIndex(LiteralArray* array, Literal literal);
-219
View File
@@ -1,219 +0,0 @@
#include "literal_dictionary.h"
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
//util functions
static void setEntryValues(_entry* entry, Literal key, Literal value) {
//much simpler now
freeLiteral(entry->key);
entry->key = copyLiteral(key);
freeLiteral(entry->value);
entry->value = copyLiteral(value);
}
static _entry* getEntryArray(_entry* array, int capacity, Literal key, unsigned int hash, bool mustExist) {
//find "key", starting at index
unsigned int index = hash % capacity;
unsigned int start = index;
//increment once, so it can't equal start
index = (index + 1) % capacity;
//literal probing and collision checking
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
_entry* entry = &array[index];
if (IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
if (IS_NULL(entry->value) && !mustExist) {
//found a truly empty bucket
return entry;
}
//else it's a tombstone - ignore
} else {
if (literalsAreEqual(key, entry->key)) {
return entry;
}
}
index = (index + 1) % capacity;
}
return NULL;
}
static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int capacity) {
//new entry space
_entry* newEntries = ALLOCATE(_entry, capacity);
for (int i = 0; i < capacity; i++) {
newEntries[i].key = TO_NULL_LITERAL;
newEntries[i].value = TO_NULL_LITERAL;
}
//move the old array into the new one
for (int i = 0; i < oldCapacity; i++) {
if (IS_NULL((*dictionaryHandle)[i].key)) {
continue;
}
//place the key and value in the new array (reusing string memory)
_entry* entry = getEntryArray(newEntries, capacity, TO_NULL_LITERAL, hashLiteral((*dictionaryHandle)[i].key), false);
entry->key = (*dictionaryHandle)[i].key;
entry->value = (*dictionaryHandle)[i].value;
}
//clear the old array
FREE_ARRAY(_entry, *dictionaryHandle, oldCapacity);
*dictionaryHandle = newEntries;
}
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
//expand array if needed
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
int oldCapacity = *capacityPtr;
*capacityPtr = GROW_CAPACITY(*capacityPtr);
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
}
_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
//true = contains increase
if (IS_NULL(entry->key)) {
setEntryValues(entry, key, value);
return true;
}
else {
setEntryValues(entry, key, value);
return false;
}
return false;
}
static void freeEntry(_entry* entry) {
freeLiteral(entry->key);
freeLiteral(entry->value);
entry->key = TO_NULL_LITERAL;
entry->value = TO_NULL_LITERAL;
}
static void freeEntryArray(_entry* array, int capacity) {
if (array == NULL) {
return;
}
for (int i = 0; i < capacity; i++) {
if (!IS_NULL(array[i].key)) {
freeEntry(&array[i]);
}
}
FREE_ARRAY(_entry, array, capacity);
}
//exposed functions
void initLiteralDictionary(LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = GROW_CAPACITY(0);
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
}
void freeLiteralDictionary(LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
}
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
return;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
return;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
return;
}
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
if (increment) {
dictionary->contains++;
dictionary->count++;
}
}
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
if (entry != NULL) {
return copyLiteral(entry->value);
}
else {
return TO_NULL_LITERAL;
}
}
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (remove)\n" RESET);
return;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (remove)\n" RESET);
return;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
return;
}
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
if (entry != NULL) {
freeEntry(entry);
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
dictionary->count--;
}
}
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
//null & not tombstoned
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
}
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
//TODO: benchmark this
#define DICTIONARY_MAX_LOAD 0.75
typedef struct _entry {
Literal key;
Literal value;
} _entry;
typedef struct LiteralDictionary {
_entry* entries;
int capacity;
int count;
int contains; //count + tombstones, for internal use
} LiteralDictionary;
TOY_API void initLiteralDictionary(LiteralDictionary* dictionary);
TOY_API void freeLiteralDictionary(LiteralDictionary* dictionary);
TOY_API void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
TOY_API Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
TOY_API void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
TOY_API bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);
+6
View File
@@ -34,6 +34,12 @@ library: $(OBJ)
static: $(OBJ)
ar crs ../$(TOY_OUTDIR)/lib$(OUTNAME).a $(OBJ)
library-release: $(OBJ) library
strip $(OUT)
static-release: $(OBJ) static
strip -d ../$(TOY_OUTDIR)/lib$(OUTNAME).a
$(OBJ): | $(ODIR)
$(ODIR):
-29
View File
@@ -1,29 +0,0 @@
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
#include <stdlib.h>
void* reallocate(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" ERROR, (int)newSize, (long int)pointer, (int)oldSize);
exit(-1);
}
return mem;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "toy_common.h"
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
#define GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
-84
View File
@@ -1,84 +0,0 @@
#pragma once
typedef enum Opcode {
OP_EOF,
//basic statements
OP_ASSERT,
OP_PRINT,
//data
OP_LITERAL,
OP_LITERAL_LONG, //for more than 256 literals in a chunk
OP_LITERAL_RAW, //forcibly get the raw value of the literal
//arithmetic operators
OP_NEGATE,
OP_ADDITION,
OP_SUBTRACTION,
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
OP_GROUPING_BEGIN,
OP_GROUPING_END,
//variable stuff
OP_SCOPE_BEGIN,
OP_SCOPE_END,
OP_TYPE_DECL, //declare a type to be used (as a literal)
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
OP_VAR_DECL, //declare a variable to be used (as a literal)
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
OP_FN_DECL, //declare a function to be used (as a literal)
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
OP_VAR_ASSIGN, //assign to a literal
OP_VAR_ADDITION_ASSIGN,
OP_VAR_SUBTRACTION_ASSIGN,
OP_VAR_MULTIPLICATION_ASSIGN,
OP_VAR_DIVISION_ASSIGN,
OP_VAR_MODULO_ASSIGN,
OP_TYPE_CAST, //temporarily change a type of an atomic value
OP_TYPE_OF, //get the type of a variable
OP_IMPORT,
OP_EXPORT,
//for indexing
OP_INDEX,
OP_INDEX_ASSIGN,
OP_INDEX_ASSIGN_INTERMEDIATE,
OP_DOT,
//comparison of values
OP_COMPARE_EQUAL,
OP_COMPARE_NOT_EQUAL,
OP_COMPARE_LESS,
OP_COMPARE_LESS_EQUAL,
OP_COMPARE_GREATER,
OP_COMPARE_GREATER_EQUAL,
OP_INVERT, //for booleans
//logical operators
OP_AND,
OP_OR,
//jumps, and conditional jumps (absolute)
OP_JUMP,
OP_IF_FALSE_JUMP,
OP_FN_CALL,
OP_FN_RETURN,
//pop the stack at the end of a complex statement
OP_POP_STACK,
//meta
OP_FN_END, //different from SECTION_END
OP_SECTION_END = 255,
//TODO: add more
} Opcode;
-1783
View File
File diff suppressed because it is too large Load Diff
-20
View File
@@ -1,20 +0,0 @@
#pragma once
#include "toy_common.h"
#include "lexer.h"
#include "ast_node.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
Lexer* lexer;
bool error; //I've had an error
bool panic; //I am processing an error
//track the last two outputs from the lexer
Token current;
Token previous;
} Parser;
TOY_API void initParser(Parser* parser, Lexer* lexer);
TOY_API void freeParser(Parser* parser);
TOY_API ASTNode* scanParser(Parser* parser);
-303
View File
@@ -1,303 +0,0 @@
#include "scope.h"
#include "memory.h"
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Scope* scope) {
scope->references--;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
}
if (scope->references > 0) {
return;
}
freeLiteralDictionary(&scope->variables);
freeLiteralDictionary(&scope->types);
FREE(Scope, scope);
}
//return false if invalid type
static bool checkType(Literal typeLiteral, Literal original, Literal value, bool constCheck) {
//for constants, fail if original != value
if (constCheck && AS_TYPE(typeLiteral).constant && !literalsAreEqual(original, value)) {
return false;
}
//for any types
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
return true;
}
//don't allow null types
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
return false;
}
//always allow null values
if (IS_NULL(value)) {
return true;
}
//for each type, if a mismatch is found, return false
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY && !IS_ARRAY(value)) {
return false;
}
if (IS_ARRAY(value)) {
//check value's type
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
return false;
}
//if null, assume it's a new entry
if (IS_NULL(original)) {
return true;
}
//check children
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
if (AS_ARRAY(original)->count <= i) {
return true; //assume new entry pushed
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(original)->literals[i], AS_ARRAY(value)->literals[i], constCheck)) {
return false;
}
}
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY && !IS_DICTIONARY(value)) {
return false;
}
if (IS_DICTIONARY(value)) {
//check value's type
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
return false;
}
//if null, assume it's a new entry to a parent
if (IS_NULL(original)) {
return true;
}
//check each child of value against the child of original
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
if (IS_NULL(AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
continue;
}
//find the internal child of original that matches this child of value
_entry* ptr = NULL;
for (int j = 0; j < AS_DICTIONARY(original)->capacity; j++) {
if (literalsAreEqual(AS_DICTIONARY(original)->entries[j].key, AS_DICTIONARY(value)->entries[i].key)) {
ptr = &AS_DICTIONARY(original)->entries[j];
break;
}
}
//if not found, assume it's a new entry
if (!ptr) {
continue;
}
//check the type of key and value
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], ptr->key, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
return false;
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], ptr->value, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
return false;
}
}
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FUNCTION && !IS_FUNCTION(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
return false;
}
return true;
}
//exposed functions
Scope* pushScope(Scope* ancestor) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = ancestor;
initLiteralDictionary(&scope->variables);
initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
return scope;
}
Scope* popScope(Scope* scope) {
if (scope == NULL) { //CAN pop a null
return NULL;
}
Scope* ret = scope->ancestor;
//BUGFIX: when freeing a scope, free the function's scopes manually
for (int i = 0; i < scope->variables.capacity; i++) {
//handle keys, just in case
if (IS_FUNCTION(scope->variables.entries[i].key)) {
popScope(AS_FUNCTION(scope->variables.entries[i].key).scope);
AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
}
if (IS_FUNCTION(scope->variables.entries[i].value)) {
popScope(AS_FUNCTION(scope->variables.entries[i].value).scope);
AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
}
}
freeAncestorChain(scope);
return ret;
}
Scope* copyScope(Scope* original) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = original->ancestor;
initLiteralDictionary(&scope->variables);
initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
//copy the contents of the dictionaries
for (int i = 0; i < original->variables.capacity; i++) {
if (!IS_NULL(original->variables.entries[i].key)) {
setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
}
}
for (int i = 0; i < original->types.capacity; i++) {
if (!IS_NULL(original->types.entries[i].key)) {
setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
}
}
return scope;
}
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
//don't redefine a variable within this scope
if (existsLiteralDictionary(&scope->variables, key)) {
return false;
}
//store the type, for later checking on assignment
setLiteralDictionary(&scope->types, key, type);
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
return true;
}
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
}
//return false if undefined, or can't be assigned
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
Literal original = getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
freeLiteral(typeLiteral);
freeLiteral(original);
return false;
}
//actually assign
setLiteralDictionary(&scope->variables, key, value);
freeLiteral(typeLiteral);
freeLiteral(original);
return true;
}
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = getLiteralDictionary(&scope->variables, key);
return true;
}
Literal getScopeType(Scope* scope, Literal key) {
//dead end
if (scope == NULL) {
return TO_NULL_LITERAL;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->types, key)) {
return getScopeType(scope->ancestor, key);
}
return getLiteralDictionary(&scope->types, key);
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include "literal_array.h"
#include "literal_dictionary.h"
typedef struct Scope {
LiteralDictionary variables; //only allow identifiers as the keys
LiteralDictionary types; //the types, indexed by identifiers
struct Scope* ancestor;
int references; //how many scopes point here
} Scope;
Scope* pushScope(Scope* scope);
Scope* popScope(Scope* scope);
Scope* copyScope(Scope* original);
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key, Literal type);
bool isDelcaredScopeVariable(Scope* scope, Literal key);
//return false if undefined
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
Literal getScopeType(Scope* scope, Literal key);
-92
View File
@@ -1,92 +0,0 @@
#pragma once
typedef enum TokenType {
//types
TOKEN_NULL,
TOKEN_BOOLEAN,
TOKEN_INTEGER,
TOKEN_FLOAT,
TOKEN_STRING,
TOKEN_ARRAY,
TOKEN_DICTIONARY,
TOKEN_FUNCTION,
TOKEN_OPAQUE,
TOKEN_ANY,
//keywords and reserved words
TOKEN_AS,
TOKEN_ASSERT,
TOKEN_BREAK,
TOKEN_CLASS,
TOKEN_CONST,
TOKEN_CONTINUE,
TOKEN_DO,
TOKEN_ELSE,
TOKEN_EXPORT,
TOKEN_FOR,
TOKEN_FOREACH,
TOKEN_IF,
TOKEN_IMPORT,
TOKEN_IN,
TOKEN_OF,
TOKEN_PRINT,
TOKEN_RETURN,
TOKEN_TYPE,
TOKEN_ASTYPE,
TOKEN_TYPEOF,
TOKEN_VAR,
TOKEN_WHILE,
//literal values
TOKEN_IDENTIFIER,
TOKEN_LITERAL_TRUE,
TOKEN_LITERAL_FALSE,
TOKEN_LITERAL_INTEGER,
TOKEN_LITERAL_FLOAT,
TOKEN_LITERAL_STRING,
//math operators
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_MULTIPLY,
TOKEN_DIVIDE,
TOKEN_MODULO,
TOKEN_PLUS_ASSIGN,
TOKEN_MINUS_ASSIGN,
TOKEN_MULTIPLY_ASSIGN,
TOKEN_DIVIDE_ASSIGN,
TOKEN_MODULO_ASSIGN,
TOKEN_PLUS_PLUS,
TOKEN_MINUS_MINUS,
TOKEN_ASSIGN,
//logical operators
TOKEN_PAREN_LEFT,
TOKEN_PAREN_RIGHT,
TOKEN_BRACKET_LEFT,
TOKEN_BRACKET_RIGHT,
TOKEN_BRACE_LEFT,
TOKEN_BRACE_RIGHT,
TOKEN_NOT,
TOKEN_NOT_EQUAL,
TOKEN_EQUAL,
TOKEN_LESS,
TOKEN_GREATER,
TOKEN_LESS_EQUAL,
TOKEN_GREATER_EQUAL,
TOKEN_AND,
TOKEN_OR,
//other operators
TOKEN_COLON,
TOKEN_SEMICOLON,
TOKEN_COMMA,
TOKEN_DOT,
TOKEN_PIPE,
TOKEN_REST,
//meta tokens
TOKEN_PASS,
TOKEN_ERROR,
TOKEN_EOF,
} TokenType;
+385
View File
@@ -0,0 +1,385 @@
#include "toy_ast_node.h"
#include "toy_memory.h"
#include <stdio.h>
#include <stdlib.h>
static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
//don't free a NULL node
if (node == NULL) {
return;
}
switch(node->type) {
case TOY_AST_NODE_ERROR:
//NO-OP
break;
case TOY_AST_NODE_LITERAL:
Toy_freeLiteral(node->atomic.literal);
break;
case TOY_AST_NODE_UNARY:
Toy_freeASTNode(node->unary.child);
break;
case TOY_AST_NODE_BINARY:
Toy_freeASTNode(node->binary.left);
Toy_freeASTNode(node->binary.right);
break;
case TOY_AST_NODE_TERNARY:
Toy_freeASTNode(node->ternary.condition);
Toy_freeASTNode(node->ternary.thenPath);
Toy_freeASTNode(node->ternary.elsePath);
break;
case TOY_AST_NODE_GROUPING:
Toy_freeASTNode(node->grouping.child);
break;
case TOY_AST_NODE_BLOCK:
for (int i = 0; i < node->block.count; i++) {
freeASTNodeCustom(node->block.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
break;
case TOY_AST_NODE_COMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeASTNodeCustom(node->compound.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
break;
case TOY_AST_NODE_PAIR:
Toy_freeASTNode(node->pair.left);
Toy_freeASTNode(node->pair.right);
break;
case TOY_AST_NODE_INDEX:
Toy_freeASTNode(node->index.first);
Toy_freeASTNode(node->index.second);
Toy_freeASTNode(node->index.third);
break;
case TOY_AST_NODE_VAR_DECL:
Toy_freeLiteral(node->varDecl.identifier);
Toy_freeLiteral(node->varDecl.typeLiteral);
Toy_freeASTNode(node->varDecl.expression);
break;
case TOY_AST_NODE_FN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeASTNodeCustom(node->fnCollection.nodes + i, false);
}
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case TOY_AST_NODE_FN_DECL:
Toy_freeLiteral(node->fnDecl.identifier);
Toy_freeASTNode(node->fnDecl.arguments);
Toy_freeASTNode(node->fnDecl.returns);
Toy_freeASTNode(node->fnDecl.block);
break;
case TOY_AST_NODE_FN_CALL:
Toy_freeASTNode(node->fnCall.arguments);
break;
case TOY_AST_NODE_FN_RETURN:
Toy_freeASTNode(node->returns.returns);
break;
case TOY_AST_NODE_IF:
Toy_freeASTNode(node->pathIf.condition);
Toy_freeASTNode(node->pathIf.thenPath);
Toy_freeASTNode(node->pathIf.elsePath);
break;
case TOY_AST_NODE_WHILE:
Toy_freeASTNode(node->pathWhile.condition);
Toy_freeASTNode(node->pathWhile.thenPath);
break;
case TOY_AST_NODE_FOR:
Toy_freeASTNode(node->pathFor.preClause);
Toy_freeASTNode(node->pathFor.postClause);
Toy_freeASTNode(node->pathFor.condition);
Toy_freeASTNode(node->pathFor.thenPath);
break;
case TOY_AST_NODE_BREAK:
//NO-OP
break;
case TOY_AST_NODE_CONTINUE:
//NO-OP
break;
case TOY_AST_NODE_PREFIX_INCREMENT:
Toy_freeLiteral(node->prefixIncrement.identifier);
break;
case TOY_AST_NODE_PREFIX_DECREMENT:
Toy_freeLiteral(node->prefixDecrement.identifier);
break;
case TOY_AST_NODE_POSTFIX_INCREMENT:
Toy_freeLiteral(node->postfixIncrement.identifier);
break;
case TOY_AST_NODE_POSTFIX_DECREMENT:
Toy_freeLiteral(node->postfixDecrement.identifier);
break;
case TOY_AST_NODE_IMPORT:
Toy_freeLiteral(node->import.identifier);
Toy_freeLiteral(node->import.alias);
break;
}
if (freeSelf) {
TOY_FREE(Toy_ASTNode, node);
}
}
void Toy_freeASTNode(Toy_ASTNode* node) {
freeASTNodeCustom(node, true);
}
//various emitters
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal) {
//allocate a new node
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
(*nodeHandle)->type = TOY_AST_NODE_LITERAL;
(*nodeHandle)->atomic.literal = Toy_copyLiteral(literal);
}
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child) {
//allocate a new node
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
(*nodeHandle)->type = TOY_AST_NODE_UNARY;
(*nodeHandle)->unary.opcode = opcode;
(*nodeHandle)->unary.child = child;
}
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_BINARY;
tmp->binary.opcode = opcode;
tmp->binary.left = *nodeHandle;
tmp->binary.right = rhs;
*nodeHandle = tmp;
}
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_TERNARY;
tmp->ternary.condition = condition;
tmp->ternary.thenPath = thenPath;
tmp->ternary.elsePath = elsePath;
*nodeHandle = tmp;
}
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_GROUPING;
tmp->grouping.child = *nodeHandle;
*nodeHandle = tmp;
}
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_BLOCK;
tmp->block.nodes = NULL; //NOTE: appended by the parser
tmp->block.capacity = 0;
tmp->block.count = 0;
*nodeHandle = tmp;
}
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_COMPOUND;
tmp->compound.literalType = literalType;
tmp->compound.nodes = NULL;
tmp->compound.capacity = 0;
tmp->compound.count = 0;
*nodeHandle = tmp;
}
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right) {
//set - assume the node has already been allocated
node->type = TOY_AST_NODE_PAIR;
node->pair.left = left;
node->pair.right = right;
}
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_INDEX;
tmp->index.first = first;
tmp->index.second = second;
tmp->index.third = third;
*nodeHandle = tmp;
}
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal typeLiteral, Toy_ASTNode* expression) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_VAR_DECL;
tmp->varDecl.identifier = identifier;
tmp->varDecl.typeLiteral = typeLiteral;
tmp->varDecl.expression = expression;
*nodeHandle = tmp;
}
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_FN_COLLECTION;
tmp->fnCollection.nodes = NULL;
tmp->fnCollection.capacity = 0;
tmp->fnCollection.count = 0;
*nodeHandle = tmp;
}
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_FN_DECL;
tmp->fnDecl.identifier = identifier;
tmp->fnDecl.arguments = arguments;
tmp->fnDecl.returns = returns;
tmp->fnDecl.block = block;
*nodeHandle = tmp;
}
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_FN_CALL;
tmp->fnCall.arguments = arguments;
tmp->fnCall.argumentCount = arguments->fnCollection.count;
*nodeHandle = tmp;
}
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_FN_RETURN;
tmp->returns.returns = returns;
*nodeHandle = tmp;
}
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_IF;
tmp->pathIf.condition = condition;
tmp->pathIf.thenPath = thenPath;
tmp->pathIf.elsePath = elsePath;
*nodeHandle = tmp;
}
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_WHILE;
tmp->pathWhile.condition = condition;
tmp->pathWhile.thenPath = thenPath;
*nodeHandle = tmp;
}
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_FOR;
tmp->pathFor.preClause = preClause;
tmp->pathFor.condition = condition;
tmp->pathFor.postClause = postClause;
tmp->pathFor.thenPath = thenPath;
*nodeHandle = tmp;
}
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_BREAK;
*nodeHandle = tmp;
}
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_CONTINUE;
*nodeHandle = tmp;
}
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_PREFIX_INCREMENT;
tmp->prefixIncrement.identifier = Toy_copyLiteral(identifier);
*nodeHandle = tmp;
}
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_PREFIX_DECREMENT;
tmp->prefixDecrement.identifier = Toy_copyLiteral(identifier);
*nodeHandle = tmp;
}
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_POSTFIX_INCREMENT;
tmp->postfixIncrement.identifier = Toy_copyLiteral(identifier);
*nodeHandle = tmp;
}
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_POSTFIX_DECREMENT;
tmp->postfixDecrement.identifier = Toy_copyLiteral(identifier);
*nodeHandle = tmp;
}
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias) {
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
tmp->type = TOY_AST_NODE_IMPORT;
tmp->import.identifier = Toy_copyLiteral(identifier);
tmp->import.alias = Toy_copyLiteral(alias);
*nodeHandle = tmp;
}
+269
View File
@@ -0,0 +1,269 @@
#pragma once
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_opcodes.h"
#include "toy_token_types.h"
//nodes are the intermediaries between parsers and compilers
typedef union Toy_private_node Toy_ASTNode;
typedef enum Toy_ASTNodeType {
TOY_AST_NODE_ERROR,
TOY_AST_NODE_LITERAL, //a simple value
TOY_AST_NODE_UNARY, //one child + opcode
TOY_AST_NODE_BINARY, //two children, left and right + opcode
TOY_AST_NODE_TERNARY, //three children, condition, then path & else path
TOY_AST_NODE_GROUPING, //one child
TOY_AST_NODE_BLOCK, //contains a sub-node array
TOY_AST_NODE_COMPOUND, //contains a sub-node array
TOY_AST_NODE_PAIR, //contains a left and right
TOY_AST_NODE_INDEX, //index a variable
TOY_AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
TOY_AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
TOY_AST_NODE_FN_COLLECTION, //parts of a function
TOY_AST_NODE_FN_CALL, //call a function
TOY_AST_NODE_FN_RETURN, //for control flow
TOY_AST_NODE_IF, //for control flow
TOY_AST_NODE_WHILE, //for control flow
TOY_AST_NODE_FOR, //for control flow
TOY_AST_NODE_BREAK, //for control flow
TOY_AST_NODE_CONTINUE, //for control flow
TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
TOY_AST_NODE_POSTFIX_DECREMENT, //decrement a variable
TOY_AST_NODE_IMPORT, //import a library
} Toy_ASTNodeType;
//literals
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal);
typedef struct Toy_NodeLiteral {
Toy_ASTNodeType type;
Toy_Literal literal;
} Toy_NodeLiteral;
//unary operator
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child);
typedef struct Toy_NodeUnary {
Toy_ASTNodeType type;
Toy_Opcode opcode;
Toy_ASTNode* child;
} Toy_NodeUnary;
//binary operator
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode); //handled node becomes lhs
typedef struct Toy_NodeBinary {
Toy_ASTNodeType type;
Toy_Opcode opcode;
Toy_ASTNode* left;
Toy_ASTNode* right;
} Toy_NodeBinary;
//ternary operator
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
typedef struct Toy_NodeTernary {
Toy_ASTNodeType type;
Toy_ASTNode* condition;
Toy_ASTNode* thenPath;
Toy_ASTNode* elsePath;
} Toy_NodeTernary;
//grouping of other AST nodes
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle);
typedef struct Toy_NodeGrouping {
Toy_ASTNodeType type;
Toy_ASTNode* child;
} Toy_NodeGrouping;
//block of statement nodes
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle);
typedef struct Toy_NodeBlock {
Toy_ASTNodeType type;
Toy_ASTNode* nodes;
int capacity;
int count;
} Toy_NodeBlock;
//compound literals (array, dictionary)
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType);
typedef struct Toy_NodeCompound {
Toy_ASTNodeType type;
Toy_LiteralType literalType;
Toy_ASTNode* nodes;
int capacity;
int count;
} Toy_NodeCompound;
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right); //NOTE: this is a set function, not an emit function
typedef struct Toy_NodePair {
Toy_ASTNodeType type;
Toy_ASTNode* left;
Toy_ASTNode* right;
} Toy_NodePair;
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third);
typedef struct Toy_NodeIndex {
Toy_ASTNodeType type;
Toy_ASTNode* first;
Toy_ASTNode* second;
Toy_ASTNode* third;
} Toy_NodeIndex;
//variable declaration
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal type, Toy_ASTNode* expression);
typedef struct Toy_NodeVarDecl {
Toy_ASTNodeType type;
Toy_Literal identifier;
Toy_Literal typeLiteral;
Toy_ASTNode* expression;
} Toy_NodeVarDecl;
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle);
typedef struct Toy_NodeFnCollection {
Toy_ASTNodeType type;
Toy_ASTNode* nodes;
int capacity;
int count;
} Toy_NodeFnCollection;
//function declaration
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block);
typedef struct Toy_NodeFnDecl {
Toy_ASTNodeType type;
Toy_Literal identifier;
Toy_ASTNode* arguments;
Toy_ASTNode* returns;
Toy_ASTNode* block;
} Toy_NodeFnDecl;
//function call
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments);
typedef struct Toy_NodeFnCall {
Toy_ASTNodeType type;
Toy_ASTNode* arguments;
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
} Toy_NodeFnCall;
//function return
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns);
typedef struct Toy_NodeFnReturn {
Toy_ASTNodeType type;
Toy_ASTNode* returns;
} Toy_NodeFnReturn;
//control flow path - if-else, while, for, break, continue, return
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath);
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath);
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle);
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle);
typedef struct Toy_NodeIf {
Toy_ASTNodeType type;
Toy_ASTNode* condition;
Toy_ASTNode* thenPath;
Toy_ASTNode* elsePath;
} Toy_NodeIf;
typedef struct Toy_NodeWhile {
Toy_ASTNodeType type;
Toy_ASTNode* condition;
Toy_ASTNode* thenPath;
} Toy_NodeWhile;
typedef struct Toy_NodeFor {
Toy_ASTNodeType type;
Toy_ASTNode* preClause;
Toy_ASTNode* condition;
Toy_ASTNode* postClause;
Toy_ASTNode* thenPath;
} Toy_NodeFor;
typedef struct Toy_NodeBreak {
Toy_ASTNodeType type;
} Toy_NodeBreak;
typedef struct Toy_NodeContinue {
Toy_ASTNodeType type;
} Toy_NodeContinue;
//pre-post increment/decrement
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
typedef struct Toy_NodePrefixIncrement {
Toy_ASTNodeType type;
Toy_Literal identifier;
} Toy_NodePrefixIncrement;
typedef struct Toy_NodePrefixDecrement {
Toy_ASTNodeType type;
Toy_Literal identifier;
} Toy_NodePrefixDecrement;
typedef struct Toy_NodePostfixIncrement {
Toy_ASTNodeType type;
Toy_Literal identifier;
} Toy_NodePostfixIncrement;
typedef struct Toy_NodePostfixDecrement {
Toy_ASTNodeType type;
Toy_Literal identifier;
} Toy_NodePostfixDecrement;
//import a library
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias);
typedef struct Toy_NodeImport {
Toy_ASTNodeType type;
Toy_Literal identifier;
Toy_Literal alias;
} Toy_NodeImport;
union Toy_private_node {
Toy_ASTNodeType type;
Toy_NodeLiteral atomic;
Toy_NodeUnary unary;
Toy_NodeBinary binary;
Toy_NodeTernary ternary;
Toy_NodeGrouping grouping;
Toy_NodeBlock block;
Toy_NodeCompound compound;
Toy_NodePair pair;
Toy_NodeIndex index;
Toy_NodeVarDecl varDecl;
Toy_NodeFnCollection fnCollection;
Toy_NodeFnDecl fnDecl;
Toy_NodeFnCall fnCall;
Toy_NodeFnReturn returns;
Toy_NodeIf pathIf;
Toy_NodeWhile pathWhile;
Toy_NodeFor pathFor;
Toy_NodeBreak pathBreak;
Toy_NodeContinue pathContinue;
Toy_NodePrefixIncrement prefixIncrement;
Toy_NodePrefixDecrement prefixDecrement;
Toy_NodePostfixIncrement postfixIncrement;
Toy_NodePostfixDecrement postfixDecrement;
Toy_NodeImport import;
};
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
+1474
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "toy_interpreter.h"
//the _index function is a historical oddity - it's used whenever a compound is indexed
int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
//globally available native functions
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
+33 -33
View File
@@ -18,74 +18,74 @@ STATIC_ASSERT(sizeof(unsigned int) == 4);
#ifndef TOY_EXPORT
//declare the singleton
Command command;
Toy_CommandLine Toy_commandLine;
void initCommand(int argc, const char* argv[]) {
void Toy_initCommandLine(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;
Toy_commandLine.error = false;
Toy_commandLine.help = false;
Toy_commandLine.version = false;
Toy_commandLine.binaryfile = NULL;
Toy_commandLine.sourcefile = NULL;
Toy_commandLine.compilefile = NULL;
Toy_commandLine.outfile = "out.tb";
Toy_commandLine.source = NULL;
Toy_commandLine.verbose = false;
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
command.error = true; //error state by default, set to false by successful flags
Toy_commandLine.error = true; //error state by default, set to false by successful flags
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
command.help = true;
command.error = false;
Toy_commandLine.help = true;
Toy_commandLine.error = false;
continue;
}
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
command.version = true;
command.error = false;
Toy_commandLine.version = true;
Toy_commandLine.error = false;
continue;
}
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
command.verbose = true;
command.error = false;
Toy_commandLine.verbose = true;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
command.sourcefile = (char*)argv[i + 1];
Toy_commandLine.sourcefile = (char*)argv[i + 1];
i++;
command.error = false;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
command.source = (char*)argv[i + 1];
Toy_commandLine.source = (char*)argv[i + 1];
i++;
command.error = false;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
command.compilefile = (char*)argv[i + 1];
Toy_commandLine.compilefile = (char*)argv[i + 1];
i++;
command.error = false;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
command.outfile = (char*)argv[i + 1];
Toy_commandLine.outfile = (char*)argv[i + 1];
i++;
command.error = false;
Toy_commandLine.error = false;
continue;
}
//option without a flag + ending in .tb = binary input
if (i < argc) {
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
command.binaryfile = (char*)argv[i];
command.error = false;
Toy_commandLine.binaryfile = (char*)argv[i];
Toy_commandLine.error = false;
continue;
}
}
@@ -95,12 +95,12 @@ void initCommand(int argc, const char* argv[]) {
}
}
void usageCommand(int argc, const char* argv[]) {
void Toy_usageCommandLine(int argc, const char* argv[]) {
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
}
void helpCommand(int argc, const char* argv[]) {
usageCommand(argc, argv);
void Toy_helpCommandLine(int argc, const char* argv[]) {
Toy_usageCommandLine(argc, argv);
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
printf("-h\t| --help\t\tShow this help then exit.\n\n");
@@ -112,9 +112,9 @@ void helpCommand(int argc, const char* argv[]) {
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[]) {
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
printf("Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios\n\n");
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
printf("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");
+8 -17
View File
@@ -5,25 +5,19 @@
#include <stdint.h>
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 6
#define TOY_VERSION_MINOR 8
#define TOY_VERSION_PATCH 2
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
//platform exports/imports
//platform-specific specifications
#if defined(__linux__)
#define TOY_API extern
#include <time.h>
#include <sys/time.h>
#elif defined(_WIN32) || defined(WIN32)
#define TOY_API
#include <time.h>
#include <sys/time.h>
#else
#define TOY_API
#include <time.h>
#include <sys/time.h>
#endif
@@ -39,16 +33,13 @@ typedef struct {
char* outfile; //defaults to out.tb
char* source;
bool verbose;
} Command;
} Toy_CommandLine;
extern Command command;
extern Toy_CommandLine Toy_commandLine;
void initCommand(int argc, const char* argv[]);
void Toy_initCommandLine(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[]);
void Toy_usageCommandLine(int argc, const char* argv[]);
void Toy_helpCommandLine(int argc, const char* argv[]);
void Toy_copyrightCommandLine(int argc, const char* argv[]);
#endif
//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
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "toy_common.h"
#include "toy_opcodes.h"
#include "toy_ast_node.h"
#include "toy_literal_array.h"
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
typedef struct Toy_Compiler {
Toy_LiteralArray literalCache;
unsigned char* bytecode;
int capacity;
int count;
bool panic;
} Toy_Compiler;
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
//embed the header, data section, code section, function section, etc.
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, 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 TOY_CC_FONT_BLACK "\033[30;"
#define TOY_CC_FONT_RED "\033[31;"
#define TOY_CC_FONT_GREEN "\033[32;"
#define TOY_CC_FONT_YELLOW "\033[33;"
#define TOY_CC_FONT_BLUE "\033[34;"
#define TOY_CC_FONT_PURPLE "\033[35;"
#define TOY_CC_FONT_DGREEN "\033[6;"
#define TOY_CC_FONT_WHITE "\033[7;"
#define TOY_CC_FONT_CYAN "\x1b[36m"
//background color
#define TOY_CC_BACK_BLACK "40m"
#define TOY_CC_BACK_RED "41m"
#define TOY_CC_BACK_GREEN "42m"
#define TOY_CC_BACK_YELLOW "43m"
#define TOY_CC_BACK_BLUE "44m"
#define TOY_CC_BACK_PURPLE "45m"
#define TOY_CC_BACK_DGREEN "46m"
#define TOY_CC_BACK_WHITE "47m"
//useful
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
#define TOY_CC_RESET "\033[0m"
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
#include "toy_scope.h"
typedef void (*Toy_PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions
typedef struct Toy_Interpreter {
//input
const unsigned char* bytecode;
int length;
int count;
int codeStart; //BUGFIX: for jumps, must be initialized to -1
Toy_LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
//operation
Toy_Scope* scope;
Toy_LiteralArray stack;
//Library APIs
Toy_LiteralDictionary* hooks;
//debug outputs
Toy_PrintFn printOutput;
Toy_PrintFn assertOutput;
Toy_PrintFn errorOutput;
int depth; //don't overflow
bool panic;
} Toy_Interpreter;
//native API
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, const char* name, Toy_NativeFn func);
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, const char* name, Toy_HookFn hook);
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, const char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
//utilities for the host program
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
//main access
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, const unsigned char* bytecode, int length); //run the code
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
+77
View File
@@ -0,0 +1,77 @@
#include "toy_keyword_types.h"
#include "toy_common.h"
#include <string.h>
Toy_KeywordType Toy_keywordTypes[] = {
//type keywords
{TOY_TOKEN_NULL, "null"},
{TOY_TOKEN_BOOLEAN, "bool"},
{TOY_TOKEN_INTEGER, "int"},
{TOY_TOKEN_FLOAT, "float"},
{TOY_TOKEN_STRING, "string"},
{TOY_TOKEN_FUNCTION, "fn"},
{TOY_TOKEN_OPAQUE, "opaque"},
{TOY_TOKEN_ANY, "any"},
//other keywords
{TOY_TOKEN_AS, "as"},
{TOY_TOKEN_ASSERT, "assert"},
{TOY_TOKEN_BREAK, "break"},
{TOY_TOKEN_CLASS, "class"},
{TOY_TOKEN_CONST, "const"},
{TOY_TOKEN_CONTINUE, "continue"},
{TOY_TOKEN_DO, "do"},
{TOY_TOKEN_ELSE, "else"},
{TOY_TOKEN_EXPORT, "export"},
{TOY_TOKEN_FOR, "for"},
{TOY_TOKEN_FOREACH, "foreach"},
{TOY_TOKEN_IF, "if"},
{TOY_TOKEN_IMPORT, "import"},
{TOY_TOKEN_IN, "in"},
{TOY_TOKEN_OF, "of"},
{TOY_TOKEN_PRINT, "print"},
{TOY_TOKEN_RETURN, "return"},
{TOY_TOKEN_TYPE, "type"},
{TOY_TOKEN_ASTYPE, "astype"},
{TOY_TOKEN_TYPEOF, "typeof"},
{TOY_TOKEN_VAR, "var"},
{TOY_TOKEN_WHILE, "while"},
//literal values
{TOY_TOKEN_LITERAL_TRUE, "true"},
{TOY_TOKEN_LITERAL_FALSE, "false"},
//meta tokens
{TOY_TOKEN_PASS, NULL},
{TOY_TOKEN_ERROR, NULL},
{TOY_TOKEN_EOF, NULL},
};
char* Toy_findKeywordByType(Toy_TokenType type) {
if (type == TOY_TOKEN_EOF) {
return "EOF";
}
for(int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (Toy_keywordTypes[i].type == type) {
return Toy_keywordTypes[i].keyword;
}
}
return NULL;
}
Toy_TokenType Toy_findTypeByKeyword(const char* keyword) {
const int length = strlen(keyword);
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (!strncmp(keyword, Toy_keywordTypes[i].keyword, length)) {
return Toy_keywordTypes[i].type;
}
}
return TOY_TOKEN_EOF;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "toy_token_types.h"
typedef struct {
Toy_TokenType type;
char* keyword;
} Toy_KeywordType;
extern Toy_KeywordType Toy_keywordTypes[];
char* Toy_findKeywordByType(Toy_TokenType type);
Toy_TokenType Toy_findTypeByKeyword(const char* keyword);
+383
View File
@@ -0,0 +1,383 @@
#include "toy_lexer.h"
#include "toy_console_colors.h"
#include "toy_keyword_types.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//static generic utility functions
static void cleanLexer(Toy_Lexer* lexer) {
lexer->source = NULL;
lexer->start = 0;
lexer->current = 0;
lexer->line = 1;
lexer->commentsEnabled = true;
}
static bool isAtEnd(Toy_Lexer* lexer) {
return lexer->source[lexer->current] == '\0';
}
static char peek(Toy_Lexer* lexer) {
return lexer->source[lexer->current];
}
static char peekNext(Toy_Lexer* lexer) {
if (isAtEnd(lexer)) return '\0';
return lexer->source[lexer->current + 1];
}
static char advance(Toy_Lexer* lexer) {
if (isAtEnd(lexer)) {
return '\0';
}
//new line
if (lexer->source[lexer->current] == '\n') {
lexer->line++;
}
lexer->current++;
return lexer->source[lexer->current - 1];
}
static void eatWhitespace(Toy_Lexer* lexer) {
const char c = peek(lexer);
switch(c) {
case ' ':
case '\r':
case '\n':
case '\t':
advance(lexer);
break;
//comments
case '/':
if (!lexer->commentsEnabled) {
return;
}
//eat the line
if (peekNext(lexer) == '/') {
while (!isAtEnd(lexer) && advance(lexer) != '\n');
break;
}
//eat the block
if (peekNext(lexer) == '*') {
advance(lexer);
advance(lexer);
while(!isAtEnd(lexer) && !(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
advance(lexer);
advance(lexer);
break;
}
return;
default:
return;
}
//tail recursion
eatWhitespace(lexer);
}
static bool isDigit(Toy_Lexer* lexer) {
return peek(lexer) >= '0' && peek(lexer) <= '9';
}
static bool isAlpha(Toy_Lexer* lexer) {
return
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
peek(lexer) == '_'
;
}
static bool match(Toy_Lexer* lexer, char c) {
if (peek(lexer) == c) {
advance(lexer);
return true;
}
return false;
}
//token generators
static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
Toy_Token token;
token.type = TOY_TOKEN_ERROR;
token.lexeme = msg;
token.length = strlen(msg);
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("err:");
Toy_printToken(&token);
}
#endif
return token;
}
static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
Toy_Token token;
token.type = type;
token.length = lexer->current - lexer->start;
token.lexeme = &lexer->source[lexer->current - token.length];
token.line = lexer->line;
#ifndef TOY_EXPORT
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
if (Toy_commandLine.verbose) {
printf("tok:");
Toy_printToken(&token);
}
#endif
return token;
}
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
type = TOY_TOKEN_LITERAL_FLOAT;
advance(lexer);
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
}
Toy_Token token;
token.type = type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
if (type == TOY_TOKEN_LITERAL_INTEGER) {
printf("int:");
} else {
printf("flt:");
}
Toy_printToken(&token);
}
#endif
return token;
}
static bool isEscapableCharacter(char c) {
switch (c) {
case 'n':
case 't':
case '\\':
case '"':
return true;
default:
return false;
}
}
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
while (!isAtEnd(lexer)) {
//stop if you've hit the terminator
if (peek(lexer) == terminator) {
advance(lexer); //eat terminator
break;
}
//skip escaped control characters
if (peek(lexer) == '\\' && isEscapableCharacter(peekNext(lexer))) {
advance(lexer);
advance(lexer);
continue;
}
//otherwise
advance(lexer);
}
if (isAtEnd(lexer)) {
return makeErrorToken(lexer, "Unterminated string");
}
Toy_Token token;
token.type = TOY_TOKEN_LITERAL_STRING;
token.lexeme = &lexer->source[lexer->start + 1];
token.length = lexer->current - lexer->start - 2;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("str:");
Toy_printToken(&token);
}
#endif
return token;
}
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
advance(lexer); //first letter can only be alpha
while(isDigit(lexer) || isAlpha(lexer)) {
advance(lexer);
}
//scan for a keyword
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
Toy_Token token;
token.type = Toy_keywordTypes[i].type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("kwd:");
Toy_printToken(&token);
}
#endif
return token;
}
}
//return an identifier
Toy_Token token;
token.type = TOY_TOKEN_IDENTIFIER;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("idf:");
Toy_printToken(&token);
}
#endif
return token;
}
//exposed functions
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
cleanLexer(lexer);
lexer->source = source;
}
Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
eatWhitespace(lexer);
lexer->start = lexer->current;
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
char c = advance(lexer);
switch(c) {
case '(': return makeToken(lexer, TOY_TOKEN_PAREN_LEFT);
case ')': return makeToken(lexer, TOY_TOKEN_PAREN_RIGHT);
case '{': return makeToken(lexer, TOY_TOKEN_BRACE_LEFT);
case '}': return makeToken(lexer, TOY_TOKEN_BRACE_RIGHT);
case '[': return makeToken(lexer, TOY_TOKEN_BRACKET_LEFT);
case ']': return makeToken(lexer, TOY_TOKEN_BRACKET_RIGHT);
case '+': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOY_TOKEN_PLUS_PLUS: TOY_TOKEN_PLUS);
case '-': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOY_TOKEN_MINUS_MINUS: TOY_TOKEN_MINUS);
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MULTIPLY_ASSIGN : TOY_TOKEN_MULTIPLY);
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_DIVIDE_ASSIGN : TOY_TOKEN_DIVIDE);
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MODULO_ASSIGN : TOY_TOKEN_MODULO);
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_NOT_EQUAL : TOY_TOKEN_NOT);
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_EQUAL : TOY_TOKEN_ASSIGN);
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_LESS_EQUAL : TOY_TOKEN_LESS);
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_GREATER_EQUAL : TOY_TOKEN_GREATER);
case '&': //TOKEN_AND not used
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
} else {
return makeToken(lexer, TOY_TOKEN_AND);
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
case ';': return makeToken(lexer, TOY_TOKEN_SEMICOLON);
case ',': return makeToken(lexer, TOY_TOKEN_COMMA);
case '.':
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
advance(lexer);
advance(lexer);
return makeToken(lexer, TOY_TOKEN_REST);
}
return makeToken(lexer, TOY_TOKEN_DOT);
case '"':
return makeString(lexer, c);
//TODO: possibly support interpolated strings
default: {
char buffer[128];
snprintf(buffer, 128, "Unexpected token: %c", c);
return makeErrorToken(lexer, buffer);
}
}
}
static void trim(char** s, int* l) { //all this to remove a newline?
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
}
//for debugging
void Toy_printToken(Toy_Token* token) {
if (token->type == TOY_TOKEN_ERROR) {
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
return;
}
printf("\t%d\t%d\t", token->type, token->line);
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
printf("%.*s\t", token->length, token->lexeme);
} else {
char* keyword = Toy_findKeywordByType(token->type);
if (keyword != NULL) {
printf("%s", keyword);
} else {
char* str = (char*)token->lexeme; //strip const-ness for trimming
int length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
}
}
printf("\n");
}
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
lexer->commentsEnabled = enabled;
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "toy_common.h"
#include "toy_token_types.h"
//lexers are bound to a string of code, and return a single token every time scan is called
typedef struct {
const char* source;
int start; //start of the token
int current; //current position of the lexer
int line; //track this for error handling
bool commentsEnabled; //BUGFIX: enable comments (disabled in repl)
} Toy_Lexer;
//tokens are intermediaries between lexers and parsers
typedef struct {
Toy_TokenType type;
const char* lexeme;
int length;
int line;
} Toy_Token;
TOY_API void Toy_initLexer(Toy_Lexer* lexer, const char* source);
Toy_Token Toy_scanLexer(Toy_Lexer* lexer);
//for debugging
void Toy_printToken(Toy_Token* token);
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
+726
View File
@@ -0,0 +1,726 @@
#include "toy_literal.h"
#include "toy_memory.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
#include "toy_scope.h"
#include "toy_console_colors.h"
#include <stdio.h>
//hash util functions
static unsigned int hashString(const char* string, int length) {
unsigned int hash = 2166136261u;
for (int i = 0; i < length; i++) {
hash *= string[i];
hash ^= 16777619;
}
return hash;
}
static unsigned int hashUInt(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
//exposed functions
void Toy_freeLiteral(Toy_Literal literal) {
//refstrings
if (TOY_IS_STRING(literal)) {
Toy_deleteRefString(TOY_AS_STRING(literal));
return;
}
if (TOY_IS_IDENTIFIER(literal)) {
Toy_deleteRefString(TOY_AS_IDENTIFIER(literal));
return;
}
//compounds
if (TOY_IS_ARRAY(literal) || literal.type == TOY_LITERAL_ARRAY_INTERMEDIATE || literal.type == TOY_LITERAL_DICTIONARY_INTERMEDIATE || literal.type == TOY_LITERAL_TYPE_INTERMEDIATE) {
Toy_freeLiteralArray(TOY_AS_ARRAY(literal));
TOY_FREE(Toy_LiteralArray, TOY_AS_ARRAY(literal));
return;
}
if (TOY_IS_DICTIONARY(literal)) {
Toy_freeLiteralDictionary(TOY_AS_DICTIONARY(literal));
TOY_FREE(Toy_LiteralDictionary, TOY_AS_DICTIONARY(literal));
return;
}
//complex literals
if (TOY_IS_FUNCTION(literal)) {
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
TOY_AS_FUNCTION(literal).scope = NULL;
TOY_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).bytecode, TOY_AS_FUNCTION(literal).length);
}
if (TOY_IS_TYPE(literal)) {
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
}
TOY_FREE_ARRAY(Toy_Literal, TOY_AS_TYPE(literal).subtypes, TOY_AS_TYPE(literal).capacity);
return;
}
}
bool Toy_private_isTruthy(Toy_Literal x) {
if (TOY_IS_NULL(x)) {
fprintf(stderr, TOY_CC_ERROR "Null is neither true nor false\n" TOY_CC_RESET);
return false;
}
if (TOY_IS_BOOLEAN(x)) {
return TOY_AS_BOOLEAN(x);
}
return true;
}
Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){TOY_LITERAL_STRING, { .string.ptr = ptr }});
}
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
return ((Toy_Literal){TOY_LITERAL_IDENTIFIER,{ .identifier.ptr = ptr, .identifier.hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }});
}
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
//grow the subtype array
if (TOY_AS_TYPE(*lit).count + 1 > TOY_AS_TYPE(*lit).capacity) {
int oldCapacity = TOY_AS_TYPE(*lit).capacity;
TOY_AS_TYPE(*lit).capacity = TOY_GROW_CAPACITY(oldCapacity);
TOY_AS_TYPE(*lit).subtypes = TOY_GROW_ARRAY(Toy_Literal, TOY_AS_TYPE(*lit).subtypes, oldCapacity, TOY_AS_TYPE(*lit).capacity);
}
//actually push
((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count++ ] = subtype;
return &((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count - 1 ];
}
Toy_Literal Toy_copyLiteral(Toy_Literal original) {
switch(original.type) {
case TOY_LITERAL_NULL:
case TOY_LITERAL_BOOLEAN:
case TOY_LITERAL_INTEGER:
case TOY_LITERAL_FLOAT:
//no copying needed
return original;
case TOY_LITERAL_STRING: {
return TOY_TO_STRING_LITERAL(Toy_copyRefString(TOY_AS_STRING(original)));
}
case TOY_LITERAL_ARRAY: {
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
//copy each element
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
}
return TOY_TO_ARRAY_LITERAL(array);
}
case TOY_LITERAL_DICTIONARY: {
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//copy each entry
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
Toy_setLiteralDictionary(dictionary, TOY_AS_DICTIONARY(original)->entries[i].key, TOY_AS_DICTIONARY(original)->entries[i].value);
}
}
return TOY_TO_DICTIONARY_LITERAL(dictionary);
}
case TOY_LITERAL_FUNCTION: {
unsigned char* buffer = TOY_ALLOCATE(unsigned char, TOY_AS_FUNCTION(original).length);
memcpy(buffer, TOY_AS_FUNCTION(original).bytecode, TOY_AS_FUNCTION(original).length);
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(buffer, TOY_AS_FUNCTION(original).length);
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
return literal;
}
case TOY_LITERAL_IDENTIFIER: {
return TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_IDENTIFIER(original)));
}
case TOY_LITERAL_TYPE: {
Toy_Literal lit = TOY_TO_TYPE_LITERAL(TOY_AS_TYPE(original).typeOf, TOY_AS_TYPE(original).constant);
for (int i = 0; i < TOY_AS_TYPE(original).count; i++) {
TOY_TYPE_PUSH_SUBTYPE(&lit, Toy_copyLiteral( ((Toy_Literal*)(TOY_AS_TYPE(original).subtypes))[i] ));
}
return lit;
}
case TOY_LITERAL_OPAQUE: {
return original; //literally a shallow copy
}
case TOY_LITERAL_ARRAY_INTERMEDIATE: {
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
//copy each element
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
Toy_pushLiteralArray(array, literal);
Toy_freeLiteral(literal);
}
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
ret.type = TOY_LITERAL_ARRAY_INTERMEDIATE;
return ret;
}
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
//copy each element
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
Toy_pushLiteralArray(array, literal);
Toy_freeLiteral(literal);
}
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
ret.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE;
return ret;
}
case TOY_LITERAL_TYPE_INTERMEDIATE: {
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
Toy_initLiteralArray(array);
//copy each element
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
Toy_pushLiteralArray(array, literal);
Toy_freeLiteral(literal);
}
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
ret.type = TOY_LITERAL_TYPE_INTERMEDIATE;
return ret;
}
case TOY_LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
case TOY_LITERAL_FUNCTION_NATIVE:
case TOY_LITERAL_FUNCTION_HOOK:
case TOY_LITERAL_INDEX_BLANK:
//no copying possible
return original;
default:
fprintf(stderr, TOY_CC_ERROR "Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
return TOY_TO_NULL_LITERAL;
}
}
bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
//utility for other things
if (lhs.type != rhs.type) {
// ints and floats are compatible
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
if (TOY_IS_INTEGER(lhs)) {
return TOY_AS_INTEGER(lhs) + TOY_AS_FLOAT(rhs);
}
else {
return TOY_AS_FLOAT(lhs) + TOY_AS_INTEGER(rhs);
}
}
return false;
}
switch(lhs.type) {
case TOY_LITERAL_NULL:
return true; //can only be true because of the check above
case TOY_LITERAL_BOOLEAN:
return TOY_AS_BOOLEAN(lhs) == TOY_AS_BOOLEAN(rhs);
case TOY_LITERAL_INTEGER:
return TOY_AS_INTEGER(lhs) == TOY_AS_INTEGER(rhs);
case TOY_LITERAL_FLOAT:
return TOY_AS_FLOAT(lhs) == TOY_AS_FLOAT(rhs);
case TOY_LITERAL_STRING:
return Toy_equalsRefString(TOY_AS_STRING(lhs), TOY_AS_STRING(rhs));
case TOY_LITERAL_ARRAY:
case TOY_LITERAL_ARRAY_INTERMEDIATE:
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
case TOY_LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
//mismatched sizes
if (TOY_AS_ARRAY(lhs)->count != TOY_AS_ARRAY(rhs)->count) {
return false;
}
//mismatched elements (in order)
for (int i = 0; i < TOY_AS_ARRAY(lhs)->count; i++) {
if (!Toy_literalsAreEqual( TOY_AS_ARRAY(lhs)->literals[i], TOY_AS_ARRAY(rhs)->literals[i] )) {
return false;
}
}
return true;
case TOY_LITERAL_DICTIONARY:
//relatively slow, especially when nested
for (int i = 0; i < TOY_AS_DICTIONARY(lhs)->capacity; i++) {
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
//check it exists in rhs
if (!Toy_existsLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key)) {
return false;
}
//compare the values
Toy_Literal val = Toy_getLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
if (!Toy_literalsAreEqual(TOY_AS_DICTIONARY(lhs)->entries[i].value, val)) {
Toy_freeLiteral(val);
return false;
}
Toy_freeLiteral(val);
}
}
return true;
case TOY_LITERAL_FUNCTION:
case TOY_LITERAL_FUNCTION_NATIVE:
case TOY_LITERAL_FUNCTION_HOOK:
return false; //functions are never equal
break;
case TOY_LITERAL_IDENTIFIER:
//check shortcuts
if (TOY_HASH_I(lhs) != TOY_HASH_I(rhs)) {
return false;
}
return Toy_equalsRefString(TOY_AS_IDENTIFIER(lhs), TOY_AS_IDENTIFIER(rhs));
case TOY_LITERAL_TYPE:
//check types
if (TOY_AS_TYPE(lhs).typeOf != TOY_AS_TYPE(rhs).typeOf) {
return false;
}
//const don't match
if (TOY_AS_TYPE(lhs).constant != TOY_AS_TYPE(rhs).constant) {
return false;
}
//check subtypes
if (TOY_AS_TYPE(lhs).count != TOY_AS_TYPE(rhs).count) {
return false;
}
//check array|dictionary signatures are the same (in order)
if (TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_DICTIONARY) {
for (int i = 0; i < TOY_AS_TYPE(lhs).count; i++) {
if (!Toy_literalsAreEqual(((Toy_Literal*)(TOY_AS_TYPE(lhs).subtypes))[i], ((Toy_Literal*)(TOY_AS_TYPE(rhs).subtypes))[i])) {
return false;
}
}
}
return true;
case TOY_LITERAL_OPAQUE:
return false; //IDK what this is!
case TOY_LITERAL_ANY:
return true;
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
fprintf(stderr, TOY_CC_ERROR "[internal] Can't compare intermediate functions\n" TOY_CC_RESET);
return false;
case TOY_LITERAL_INDEX_BLANK:
return false;
default:
//should never be seen
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in equality: %d\n" TOY_CC_RESET, lhs.type);
return false;
}
return false;
}
int Toy_hashLiteral(Toy_Literal lit) {
switch(lit.type) {
case TOY_LITERAL_NULL:
return 0;
case TOY_LITERAL_BOOLEAN:
return TOY_AS_BOOLEAN(lit) ? 1 : 0;
case TOY_LITERAL_INTEGER:
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
case TOY_LITERAL_FLOAT:
return hashUInt(*(unsigned int*)(&TOY_AS_FLOAT(lit)));
case TOY_LITERAL_STRING:
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
case TOY_LITERAL_ARRAY: {
unsigned int res = 0;
for (int i = 0; i < TOY_AS_ARRAY(lit)->count; i++) {
res += Toy_hashLiteral(TOY_AS_ARRAY(lit)->literals[i]);
}
return hashUInt(res);
}
case TOY_LITERAL_DICTIONARY: {
unsigned int res = 0;
for (int i = 0; i < TOY_AS_DICTIONARY(lit)->capacity; i++) {
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].key);
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].value);
}
}
return hashUInt(res);
}
case TOY_LITERAL_FUNCTION:
case TOY_LITERAL_FUNCTION_NATIVE:
case TOY_LITERAL_FUNCTION_HOOK:
return 0; //can't hash these
case TOY_LITERAL_IDENTIFIER:
return TOY_HASH_I(lit); //pre-computed
case TOY_LITERAL_TYPE:
return TOY_AS_TYPE(lit).typeOf; //nothing else I can do
case TOY_LITERAL_OPAQUE:
case TOY_LITERAL_ANY:
return -1;
default:
//should never bee seen
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
return 0;
}
}
//utils
static void stdoutWrapper(const char* output) {
printf("%s", output);
}
//buffer the prints
static char* globalPrintBuffer = NULL;
static size_t globalPrintCapacity = 0;
static size_t globalPrintCount = 0;
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
static char quotes = 0; //set to 0 to not show string quotes
static void printToBuffer(const char* str) {
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
int oldCapacity = globalPrintCapacity;
globalPrintCapacity = TOY_GROW_CAPACITY(globalPrintCapacity);
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
globalPrintCount += strlen(str);
}
//exposed functions
void Toy_printLiteral(Toy_Literal literal) {
Toy_printLiteralCustom(literal, stdoutWrapper);
}
void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
switch(literal.type) {
case TOY_LITERAL_NULL:
printFn("null");
break;
case TOY_LITERAL_BOOLEAN:
printFn(TOY_AS_BOOLEAN(literal) ? "true" : "false");
break;
case TOY_LITERAL_INTEGER: {
char buffer[256];
snprintf(buffer, 256, "%d", TOY_AS_INTEGER(literal));
printFn(buffer);
}
break;
case TOY_LITERAL_FLOAT: {
char buffer[256];
if (TOY_AS_FLOAT(literal) - (int)TOY_AS_FLOAT(literal)) {
snprintf(buffer, 256, "%g", TOY_AS_FLOAT(literal));
}
else {
snprintf(buffer, 256, "%.1f", TOY_AS_FLOAT(literal));
}
printFn(buffer);
}
break;
case TOY_LITERAL_STRING: {
char buffer[TOY_MAX_STRING_LENGTH];
if (!quotes) {
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
}
else {
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
}
printFn(buffer);
}
break;
case TOY_LITERAL_ARRAY: {
Toy_LiteralArray* ptr = TOY_AS_ARRAY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
printToBuffer("[");
for (int i = 0; i < ptr->count; i++) {
quotes = '"';
Toy_printLiteralCustom(ptr->literals[i], printToBuffer);
if (i + 1 < ptr->count) {
printToBuffer(",");
}
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case TOY_LITERAL_DICTIONARY: {
Toy_LiteralDictionary* ptr = TOY_AS_DICTIONARY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
int delimCount = 0;
printToBuffer("[");
for (int i = 0; i < ptr->capacity; i++) {
if (TOY_IS_NULL(ptr->entries[i].key)) {
continue;
}
if (delimCount++ > 0) {
printToBuffer(",");
}
quotes = '"';
Toy_printLiteralCustom(ptr->entries[i].key, printToBuffer);
printToBuffer(":");
quotes = '"';
Toy_printLiteralCustom(ptr->entries[i].value, printToBuffer);
}
//empty dicts MUST have a ":" printed
if (ptr->count == 0) {
printToBuffer(":");
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case TOY_LITERAL_FUNCTION:
case TOY_LITERAL_FUNCTION_NATIVE:
case TOY_LITERAL_FUNCTION_HOOK:
printFn("(function)");
break;
case TOY_LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", (int)Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
printFn(buffer);
}
break;
case TOY_LITERAL_TYPE: {
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the type correctly
printToBuffer("<");
switch(TOY_AS_TYPE(literal).typeOf) {
case TOY_LITERAL_NULL:
printToBuffer("null");
break;
case TOY_LITERAL_BOOLEAN:
printToBuffer("bool");
break;
case TOY_LITERAL_INTEGER:
printToBuffer("int");
break;
case TOY_LITERAL_FLOAT:
printToBuffer("float");
break;
case TOY_LITERAL_STRING:
printToBuffer("string");
break;
case TOY_LITERAL_ARRAY:
//print all in the array
printToBuffer("[");
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
}
printToBuffer("]");
break;
case TOY_LITERAL_DICTIONARY:
printToBuffer("[");
for (int i = 0; i < TOY_AS_TYPE(literal).count; i += 2) {
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
printToBuffer(":");
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
}
printToBuffer("]");
break;
case TOY_LITERAL_FUNCTION:
printToBuffer("function");
break;
case TOY_LITERAL_FUNCTION_NATIVE:
printToBuffer("native");
break;
case TOY_LITERAL_IDENTIFIER:
printToBuffer("identifier");
break;
case TOY_LITERAL_TYPE:
printToBuffer("type");
break;
case TOY_LITERAL_OPAQUE:
printToBuffer("opaque");
break;
case TOY_LITERAL_ANY:
printToBuffer("any");
break;
default:
//should never be seen
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print type: %d\n" TOY_CC_RESET, TOY_AS_TYPE(literal).typeOf);
}
//const (printed last)
if (TOY_AS_TYPE(literal).constant) {
printToBuffer(" const");
}
printToBuffer(">");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case TOY_LITERAL_TYPE_INTERMEDIATE:
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
printFn("Unprintable literal found");
break;
case TOY_LITERAL_OPAQUE:
printFn("(opaque)");
break;
case TOY_LITERAL_ANY:
printFn("(any)");
break;
default:
//should never be seen
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print: %d\n" TOY_CC_RESET, literal.type);
}
}
+149
View File
@@ -0,0 +1,149 @@
#pragma once
#include "toy_common.h"
#include "toy_refstring.h"
//forward delcare stuff
struct Toy_Literal;
struct Toy_Interpreter;
struct Toy_LiteralArray;
typedef int (*Toy_NativeFn)(struct Toy_Interpreter* interpreter, struct Toy_LiteralArray* arguments);
typedef int (*Toy_HookFn)(struct Toy_Interpreter* interpreter, struct Toy_Literal identifier, struct Toy_Literal alias);
#include <string.h>
typedef enum {
TOY_LITERAL_NULL,
TOY_LITERAL_BOOLEAN,
TOY_LITERAL_INTEGER,
TOY_LITERAL_FLOAT,
TOY_LITERAL_STRING,
TOY_LITERAL_ARRAY,
TOY_LITERAL_DICTIONARY,
TOY_LITERAL_FUNCTION,
TOY_LITERAL_IDENTIFIER,
TOY_LITERAL_TYPE,
TOY_LITERAL_OPAQUE,
TOY_LITERAL_ANY,
//these are meta-level types - not for general use
TOY_LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
TOY_LITERAL_ARRAY_INTERMEDIATE, //used to process arrays in the compiler only
TOY_LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
TOY_LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
TOY_LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
TOY_LITERAL_FUNCTION_NATIVE, //for handling native functions only
TOY_LITERAL_FUNCTION_HOOK, //for handling hook functions within literals only
TOY_LITERAL_INDEX_BLANK, //for blank indexing i.e. arr[:]
} Toy_LiteralType;
typedef struct Toy_Literal {
Toy_LiteralType type;
union {
bool boolean;
int integer;
float number;
struct {
Toy_RefString* ptr;
//string hash?
} string;
void* array;
void* dictionary;
struct {
void* bytecode;
Toy_NativeFn native; //already a pointer
Toy_HookFn hook; //already a pointer
void* scope;
int length;
} function;
struct { //for variable names
Toy_RefString* ptr;
int hash;
} identifier;
struct {
Toy_LiteralType typeOf;
bool constant;
void* subtypes; //for nested types caused by compounds
int capacity;
int count;
} type;
struct {
void* ptr;
int tag;
} opaque;
} as;
} Toy_Literal;
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
#define TOY_IS_FLOAT(value) ((value).type == TOY_LITERAL_FLOAT)
#define TOY_IS_STRING(value) ((value).type == TOY_LITERAL_STRING)
#define TOY_IS_ARRAY(value) ((value).type == TOY_LITERAL_ARRAY)
#define TOY_IS_DICTIONARY(value) ((value).type == TOY_LITERAL_DICTIONARY)
#define TOY_IS_FUNCTION(value) ((value).type == TOY_LITERAL_FUNCTION)
#define TOY_IS_FUNCTION_NATIVE(value) ((value).type == TOY_LITERAL_FUNCTION_NATIVE)
#define TOY_IS_FUNCTION_HOOK(value) ((value).type == TOY_LITERAL_FUNCTION_HOOK)
#define TOY_IS_IDENTIFIER(value) ((value).type == TOY_LITERAL_IDENTIFIER)
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
#define TOY_AS_INTEGER(value) ((value).as.integer)
#define TOY_AS_FLOAT(value) ((value).as.number)
#define TOY_AS_STRING(value) ((value).as.string.ptr)
#define TOY_AS_ARRAY(value) ((Toy_LiteralArray*)((value).as.array))
#define TOY_AS_DICTIONARY(value) ((Toy_LiteralDictionary*)((value).as.dictionary))
#define TOY_AS_FUNCTION(value) ((value).as.function)
#define TOY_AS_FUNCTION_NATIVE(value) ((value).as.function.native)
#define TOY_AS_FUNCTION_HOOK(value) ((value).as.function.hook)
#define TOY_AS_IDENTIFIER(value) ((value).as.identifier.ptr)
#define TOY_AS_TYPE(value) ((value).as.type)
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
#define TOY_TO_NULL_LITERAL ((Toy_Literal){TOY_LITERAL_NULL, { .integer = 0 }})
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){TOY_LITERAL_BOOLEAN, { .boolean = value }})
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){TOY_LITERAL_INTEGER, { .integer = value }})
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FLOAT, { .number = value }})
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_ARRAY, { .array = value }})
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_DICTIONARY, { .dictionary = value }})
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){TOY_LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
#define TOY_TO_FUNCTION_NATIVE_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FUNCTION_NATIVE, { .function.native = value, .function.scope = NULL, .function.length = 0 }})
#define TOY_TO_FUNCTION_HOOK_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FUNCTION_HOOK, { .function.hook = value, .function.scope = NULL, .function.length = 0 }})
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){ TOY_LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){ TOY_LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
//BUGFIX: For blank indexing
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){TOY_LITERAL_INDEX_BLANK, { .integer = 0 }})
TOY_API void Toy_freeLiteral(Toy_Literal literal);
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
#define TOY_MAX_STRING_LENGTH 4096
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
//BUGFIX: macros are not functions
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
TOY_API Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
//utils
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
TOY_API int Toy_hashLiteral(Toy_Literal lit);
TOY_API void Toy_printLiteral(Toy_Literal literal);
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*));
+98
View File
@@ -0,0 +1,98 @@
#include "toy_literal_array.h"
#include "toy_memory.h"
#include <stdio.h>
#include <string.h>
//exposed functions
void Toy_initLiteralArray(Toy_LiteralArray* array) {
array->capacity = 0;
array->count = 0;
array->literals = NULL;
}
void Toy_freeLiteralArray(Toy_LiteralArray* array) {
//clean up memory
for(int i = 0; i < array->count; i++) {
Toy_freeLiteral(array->literals[i]);
}
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
Toy_initLiteralArray(array);
}
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
if (array->capacity < array->count + 1) {
int oldCapacity = array->capacity;
array->capacity = TOY_GROW_CAPACITY(oldCapacity);
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, oldCapacity, array->capacity);
}
array->literals[array->count] = Toy_copyLiteral(literal);
return array->count++;
}
Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
if (array->count <= 0) {
return TOY_TO_NULL_LITERAL;
}
//get the return
Toy_Literal ret = array->literals[array->count-1];
//null the existing data
array->literals[array->count-1] = TOY_TO_NULL_LITERAL;
array->count--;
return ret;
}
//find a literal in the array that matches the "literal" argument
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
for (int i = 0; i < array->count; i++) {
//not the same type
if (array->literals[i].type != literal.type) {
continue;
}
//types match?
if (Toy_literalsAreEqual(array->literals[i], literal)) {
return i;
}
}
return -1;
}
bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value) {
if (!TOY_IS_INTEGER(index)) {
return false;
}
int idx = TOY_AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return false;
}
Toy_freeLiteral(array->literals[idx]);
array->literals[idx] = Toy_copyLiteral(value);
return true;
}
Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index) {
if (!TOY_IS_INTEGER(index)) {
return TOY_TO_NULL_LITERAL;
}
int idx = TOY_AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return TOY_TO_NULL_LITERAL;
}
return Toy_copyLiteral(array->literals[idx]);
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "toy_common.h"
#include "toy_literal.h"
typedef struct Toy_LiteralArray {
Toy_Literal* literals;
int capacity;
int count;
} Toy_LiteralArray;
TOY_API void Toy_initLiteralArray(Toy_LiteralArray* array);
TOY_API void Toy_freeLiteralArray(Toy_LiteralArray* array);
TOY_API int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal);
TOY_API Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array);
TOY_API bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value);
TOY_API Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index);
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
+219
View File
@@ -0,0 +1,219 @@
#include "toy_literal_dictionary.h"
#include "toy_memory.h"
#include "toy_console_colors.h"
#include <stdio.h>
//util functions
static void setEntryValues(Toy_private_entry* entry, Toy_Literal key, Toy_Literal value) {
//much simpler now
Toy_freeLiteral(entry->key);
entry->key = Toy_copyLiteral(key);
Toy_freeLiteral(entry->value);
entry->value = Toy_copyLiteral(value);
}
static Toy_private_entry* getEntryArray(Toy_private_entry* array, int capacity, Toy_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
Toy_private_entry* entry = &array[index];
if (TOY_IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
if (TOY_IS_NULL(entry->value) && !mustExist) {
//found a truly empty bucket
return entry;
}
//else it's a tombstone - ignore
} else {
if (Toy_literalsAreEqual(key, entry->key)) {
return entry;
}
}
index = (index + 1) % capacity;
}
return NULL;
}
static void adjustEntryCapacity(Toy_private_entry** dictionaryHandle, int oldCapacity, int capacity) {
//new entry space
Toy_private_entry* newEntries = TOY_ALLOCATE(Toy_private_entry, capacity);
for (int i = 0; i < capacity; i++) {
newEntries[i].key = TOY_TO_NULL_LITERAL;
newEntries[i].value = TOY_TO_NULL_LITERAL;
}
//move the old array into the new one
for (int i = 0; i < oldCapacity; i++) {
if (TOY_IS_NULL((*dictionaryHandle)[i].key)) {
continue;
}
//place the key and value in the new array (reusing string memory)
Toy_private_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
entry->key = (*dictionaryHandle)[i].key;
entry->value = (*dictionaryHandle)[i].value;
}
//clear the old array
TOY_FREE_ARRAY(Toy_private_entry, *dictionaryHandle, oldCapacity);
*dictionaryHandle = newEntries;
}
static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
//expand array if needed
if (contains + 1 > *capacityPtr * TOY_DICTIONARY_MAX_LOAD) {
int oldCapacity = *capacityPtr;
*capacityPtr = TOY_GROW_CAPACITY(*capacityPtr);
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
}
Toy_private_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
//true = contains increase
if (TOY_IS_NULL(entry->key)) {
setEntryValues(entry, key, value);
return true;
}
else {
setEntryValues(entry, key, value);
return false;
}
return false;
}
static void freeEntry(Toy_private_entry* entry) {
Toy_freeLiteral(entry->key);
Toy_freeLiteral(entry->value);
entry->key = TOY_TO_NULL_LITERAL;
entry->value = TOY_TO_NULL_LITERAL;
}
static void freeEntryArray(Toy_private_entry* array, int capacity) {
if (array == NULL) {
return;
}
for (int i = 0; i < capacity; i++) {
if (!TOY_IS_NULL(array[i].key)) {
freeEntry(&array[i]);
}
}
TOY_FREE_ARRAY(Toy_private_entry, array, capacity);
}
//exposed functions
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = TOY_GROW_CAPACITY(0);
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
}
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
}
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
if (TOY_IS_NULL(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (set)\n" TOY_CC_RESET);
return;
}
//BUGFIX: Can't hash a function
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (set)\n" TOY_CC_RESET);
return;
}
if (TOY_IS_OPAQUE(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (set)\n" TOY_CC_RESET);
return;
}
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, Toy_hashLiteral(key));
if (increment) {
dictionary->contains++;
dictionary->count++;
}
}
Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
if (TOY_IS_NULL(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (get)\n" TOY_CC_RESET);
return TOY_TO_NULL_LITERAL;
}
//BUGFIX: Can't hash a function
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (get)\n" TOY_CC_RESET);
return TOY_TO_NULL_LITERAL;
}
if (TOY_IS_OPAQUE(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (get)\n" TOY_CC_RESET);
return TOY_TO_NULL_LITERAL;
}
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
if (entry != NULL) {
return Toy_copyLiteral(entry->value);
}
else {
return TOY_TO_NULL_LITERAL;
}
}
void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
if (TOY_IS_NULL(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (remove)\n" TOY_CC_RESET);
return;
}
//BUGFIX: Can't hash a function
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (remove)\n" TOY_CC_RESET);
return;
}
if (TOY_IS_OPAQUE(key)) {
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (remove)\n" TOY_CC_RESET);
return;
}
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
if (entry != NULL) {
freeEntry(entry);
entry->value = TOY_TO_BOOLEAN_LITERAL(true); //tombstone
dictionary->count--;
}
}
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
//null & not tombstoned
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
return !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "toy_common.h"
#include "toy_literal.h"
//TODO: benchmark this
#define TOY_DICTIONARY_MAX_LOAD 0.75
typedef struct Toy_private_entry {
Toy_Literal key;
Toy_Literal value;
} Toy_private_entry;
typedef struct Toy_LiteralDictionary {
Toy_private_entry* entries;
int capacity;
int count;
int contains; //count + tombstones, for internal use
} Toy_LiteralDictionary;
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
+53
View File
@@ -0,0 +1,53 @@
#include "toy_memory.h"
#include "toy_refstring.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
//default allocator
void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
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, TOY_CC_ERROR "[internal] Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
exit(-1);
}
return mem;
}
//static variables
static Toy_MemoryAllocatorFn allocator = Toy_private_defaultMemoryAllocator;
//exposed API
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
return allocator(pointer, oldSize, newSize);
}
void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
if (fn == NULL) {
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't be null)\n" TOY_CC_RESET);
exit(-1);
}
if (fn == Toy_reallocate) {
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't loop the Toy_reallocate function)\n" TOY_CC_RESET);
exit(-1);
}
allocator = fn;
Toy_setRefStringAllocatorFn(fn);
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "toy_common.h"
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
//implementation details
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
//assign the memory allocator
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
+87
View File
@@ -0,0 +1,87 @@
#pragma once
typedef enum Toy_Opcode {
TOY_OP_EOF,
//basic statements
TOY_OP_ASSERT,
TOY_OP_PRINT,
//data
TOY_OP_LITERAL,
TOY_OP_LITERAL_LONG, //for more than 256 literals in a chunk
TOY_OP_LITERAL_RAW, //forcibly get the raw value of the literal
//arithmetic operators
TOY_OP_NEGATE,
TOY_OP_ADDITION,
TOY_OP_SUBTRACTION,
TOY_OP_MULTIPLICATION,
TOY_OP_DIVISION,
TOY_OP_MODULO,
TOY_OP_GROUPING_BEGIN,
TOY_OP_GROUPING_END,
//variable stuff
TOY_OP_SCOPE_BEGIN,
TOY_OP_SCOPE_END,
TOY_OP_TYPE_DECL, //declare a type to be used (as a literal)
TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
TOY_OP_VAR_DECL, //declare a variable to be used (as a literal)
TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
TOY_OP_FN_DECL, //declare a function to be used (as a literal)
TOY_OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
TOY_OP_VAR_ASSIGN, //assign to a literal
TOY_OP_VAR_ADDITION_ASSIGN,
TOY_OP_VAR_SUBTRACTION_ASSIGN,
TOY_OP_VAR_MULTIPLICATION_ASSIGN,
TOY_OP_VAR_DIVISION_ASSIGN,
TOY_OP_VAR_MODULO_ASSIGN,
TOY_OP_TYPE_CAST, //temporarily change a type of an atomic value
TOY_OP_TYPE_OF, //get the type of a variable
TOY_OP_IMPORT,
TOY_OP_EXPORT_removed,
//for indexing
TOY_OP_INDEX,
TOY_OP_INDEX_ASSIGN,
TOY_OP_INDEX_ASSIGN_INTERMEDIATE,
TOY_OP_DOT,
//comparison of values
TOY_OP_COMPARE_EQUAL,
TOY_OP_COMPARE_NOT_EQUAL,
TOY_OP_COMPARE_LESS,
TOY_OP_COMPARE_LESS_EQUAL,
TOY_OP_COMPARE_GREATER,
TOY_OP_COMPARE_GREATER_EQUAL,
TOY_OP_INVERT, //for booleans
//logical operators
TOY_OP_AND,
TOY_OP_OR,
//jumps, and conditional jumps (absolute)
TOY_OP_JUMP,
TOY_OP_IF_FALSE_JUMP,
TOY_OP_FN_CALL,
TOY_OP_FN_RETURN,
//pop the stack at the end of a complex statement
TOY_OP_POP_STACK,
//ternary shorthand
TOY_OP_TERNARY,
//meta
TOY_OP_FN_END, //different from SECTION_END
TOY_OP_SECTION_END = 255,
//TODO: add more
} Toy_Opcode;
+1855
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 "toy_lexer.h"
#include "toy_ast_node.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
Toy_Lexer* lexer;
bool error; //I've had an error
bool panic; //I am processing an error
//track the last two outputs from the lexer
Toy_Token current;
Toy_Token previous;
} Toy_Parser;
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
TOY_API void Toy_freeParser(Toy_Parser* parser);
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
+95
View File
@@ -0,0 +1,95 @@
#include "toy_refstring.h"
#include <string.h>
//memory allocation
extern void* Toy_private_defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize);
static Toy_RefStringAllocatorFn allocate = Toy_private_defaultMemoryAllocator;
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn allocator) {
allocate = allocator;
}
//API
Toy_RefString* Toy_createRefString(const char* cstring) {
size_t length = strlen(cstring);
return Toy_createRefStringLength(cstring, length);
}
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length) {
//allocate the memory area (including metadata space)
Toy_RefString* refString = allocate(NULL, 0, sizeof(size_t) + sizeof(int) + sizeof(char) * (length + 1));
if (refString == NULL) {
return NULL;
}
//set the data
refString->refCount = 1;
refString->length = length;
strncpy(refString->data, cstring, refString->length);
refString->data[refString->length] = '\0'; //string terminator
return refString;
}
void Toy_deleteRefString(Toy_RefString* refString) {
//decrement, then check
refString->refCount--;
if (refString->refCount <= 0) {
allocate(refString, sizeof(size_t) + sizeof(int) + sizeof(char) * (refString->length + 1), 0);
}
}
int Toy_countRefString(Toy_RefString* refString) {
return refString->refCount;
}
size_t Toy_lengthRefString(Toy_RefString* refString) {
return refString->length;
}
Toy_RefString* Toy_copyRefString(Toy_RefString* refString) {
//Cheaty McCheater Face
refString->refCount++;
return refString;
}
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString) {
//create a new string, with a new refCount
return Toy_createRefStringLength(refString->data, refString->length);
}
const char* Toy_toCString(Toy_RefString* refString) {
return refString->data;
}
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs) {
//same pointer
if (lhs == rhs) {
return true;
}
//different length
if (lhs->length != rhs->length) {
return false;
}
//same string
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
}
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
//get the rhs length
size_t length = strlen(cstring);
//different length
if (lhs->length != length) {
return false;
}
//same string
return strncmp(lhs->data, cstring, lhs->length) == 0;
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
//memory allocation hook
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
//the RefString structure
typedef struct Toy_RefString {
size_t length;
int refCount;
char data[];
} Toy_RefString;
//API
Toy_RefString* Toy_createRefString(const char* cstring);
Toy_RefString* Toy_createRefStringLength(const char* cstring, size_t length);
void Toy_deleteRefString(Toy_RefString* refString);
int Toy_countRefString(Toy_RefString* refString);
size_t Toy_lengthRefString(Toy_RefString* refString);
Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
const char* Toy_toCString(Toy_RefString* refString);
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
+324
View File
@@ -0,0 +1,324 @@
#include "toy_scope.h"
#include "toy_memory.h"
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Toy_Scope* scope) {
scope->references--;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
}
if (scope->references > 0) {
return;
}
Toy_freeLiteralDictionary(&scope->variables);
Toy_freeLiteralDictionary(&scope->types);
TOY_FREE(Toy_Scope, scope);
}
//return false if invalid type
static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal value, bool constCheck) {
//for constants, fail if original != value
if (constCheck && TOY_AS_TYPE(typeLiteral).constant && !Toy_literalsAreEqual(original, value)) {
return false;
}
//for any types
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ANY) {
return true;
}
//don't allow null types
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_NULL) {
return false;
}
//always allow null values
if (TOY_IS_NULL(value)) {
return true;
}
//for each type, if a mismatch is found, return false
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_BOOLEAN && !TOY_IS_BOOLEAN(value)) {
return false;
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_INTEGER && !TOY_IS_INTEGER(value)) {
return false;
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FLOAT && !TOY_IS_FLOAT(value)) {
return false;
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_STRING && !TOY_IS_STRING(value)) {
return false;
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY && !TOY_IS_ARRAY(value)) {
return false;
}
if (TOY_IS_ARRAY(value)) {
//check value's type
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_ARRAY) {
return false;
}
//if null, assume it's a new array variable that needs checking
if (TOY_IS_NULL(original)) {
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
if (!checkType( ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_ARRAY(value)->literals[i], constCheck)) {
return false;
}
}
return true;
}
//check children
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
if (TOY_AS_ARRAY(original)->count <= i) {
return true; //assume new entry pushed
}
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_AS_ARRAY(original)->literals[i], TOY_AS_ARRAY(value)->literals[i], constCheck)) {
return false;
}
}
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_DICTIONARY && !TOY_IS_DICTIONARY(value)) {
return false;
}
if (TOY_IS_DICTIONARY(value)) {
//check value's type
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_DICTIONARY) {
return false;
}
//if null, assume it's a new dictionary variable that needs checking
if (TOY_IS_NULL(original)) {
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
//check the type of key and value
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
return false;
}
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
return false;
}
}
return true;
}
//check each child of value against the child of original
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
if (TOY_IS_NULL(TOY_AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
continue;
}
//find the internal child of original that matches this child of value
Toy_private_entry* ptr = NULL;
for (int j = 0; j < TOY_AS_DICTIONARY(original)->capacity; j++) {
if (Toy_literalsAreEqual(TOY_AS_DICTIONARY(original)->entries[j].key, TOY_AS_DICTIONARY(value)->entries[i].key)) {
ptr = &TOY_AS_DICTIONARY(original)->entries[j];
break;
}
}
//if not found, assume it's a new entry
if (!ptr) {
continue;
}
//check the type of key and value
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], ptr->key, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
return false;
}
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], ptr->value, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
return false;
}
}
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FUNCTION && !TOY_IS_FUNCTION(value)) {
return false;
}
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_TYPE && !TOY_IS_TYPE(value)) {
return false;
}
return true;
}
//exposed functions
Toy_Scope* Toy_pushScope(Toy_Scope* ancestor) {
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
scope->ancestor = ancestor;
Toy_initLiteralDictionary(&scope->variables);
Toy_initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
return scope;
}
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
if (scope == NULL) { //CAN pop a null
return NULL;
}
Toy_Scope* ret = scope->ancestor;
//BUGFIX: when freeing a scope, free the function's scopes manually
for (int i = 0; i < scope->variables.capacity; i++) {
//handle keys, just in case
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].key).scope);
TOY_AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
}
if (TOY_IS_FUNCTION(scope->variables.entries[i].value)) {
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].value).scope);
TOY_AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
}
}
freeAncestorChain(scope);
return ret;
}
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
scope->ancestor = original->ancestor;
Toy_initLiteralDictionary(&scope->variables);
Toy_initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
//copy the contents of the dictionaries
for (int i = 0; i < original->variables.capacity; i++) {
if (!TOY_IS_NULL(original->variables.entries[i].key)) {
Toy_setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
}
}
for (int i = 0; i < original->types.capacity; i++) {
if (!TOY_IS_NULL(original->types.entries[i].key)) {
Toy_setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
}
}
return scope;
}
//returns false if error
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type) {
//don't redefine a variable within this scope
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
return false;
}
if (!TOY_IS_TYPE(type)) {
return false;
}
//store the type, for later checking on assignment
Toy_setLiteralDictionary(&scope->types, key, type);
Toy_setLiteralDictionary(&scope->variables, key, TOY_TO_NULL_LITERAL);
return true;
}
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
}
//return false if undefined, or can't be assigned
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return false;
}
//actually assign
Toy_setLiteralDictionary(&scope->variables, key, value);
Toy_freeLiteral(typeLiteral);
Toy_freeLiteral(original);
return true;
}
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
return true;
}
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
//dead end
if (scope == NULL) {
return TOY_TO_NULL_LITERAL;
}
//if it's not in this scope, keep searching up the chain
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
return Toy_getScopeType(scope->ancestor, key);
}
return Toy_getLiteralDictionary(&scope->types, key);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
typedef struct Toy_Scope {
Toy_LiteralDictionary variables; //only allow identifiers as the keys
Toy_LiteralDictionary types; //the types, indexed by identifiers
struct Toy_Scope* ancestor;
int references; //how many scopes point here
} Toy_Scope;
Toy_Scope* Toy_pushScope(Toy_Scope* scope);
Toy_Scope* Toy_popScope(Toy_Scope* scope);
Toy_Scope* Toy_copyScope(Toy_Scope* original);
//returns false if error
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
//return false if undefined
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
+93
View File
@@ -0,0 +1,93 @@
#pragma once
typedef enum Toy_TokenType {
//types
TOY_TOKEN_NULL,
TOY_TOKEN_BOOLEAN,
TOY_TOKEN_INTEGER,
TOY_TOKEN_FLOAT,
TOY_TOKEN_STRING,
TOY_TOKEN_ARRAY,
TOY_TOKEN_DICTIONARY,
TOY_TOKEN_FUNCTION,
TOY_TOKEN_OPAQUE,
TOY_TOKEN_ANY,
//keywords and reserved words
TOY_TOKEN_AS,
TOY_TOKEN_ASSERT,
TOY_TOKEN_BREAK,
TOY_TOKEN_CLASS,
TOY_TOKEN_CONST,
TOY_TOKEN_CONTINUE,
TOY_TOKEN_DO,
TOY_TOKEN_ELSE,
TOY_TOKEN_EXPORT,
TOY_TOKEN_FOR,
TOY_TOKEN_FOREACH,
TOY_TOKEN_IF,
TOY_TOKEN_IMPORT,
TOY_TOKEN_IN,
TOY_TOKEN_OF,
TOY_TOKEN_PRINT,
TOY_TOKEN_RETURN,
TOY_TOKEN_TYPE,
TOY_TOKEN_ASTYPE,
TOY_TOKEN_TYPEOF,
TOY_TOKEN_VAR,
TOY_TOKEN_WHILE,
//literal values
TOY_TOKEN_IDENTIFIER,
TOY_TOKEN_LITERAL_TRUE,
TOY_TOKEN_LITERAL_FALSE,
TOY_TOKEN_LITERAL_INTEGER,
TOY_TOKEN_LITERAL_FLOAT,
TOY_TOKEN_LITERAL_STRING,
//math operators
TOY_TOKEN_PLUS,
TOY_TOKEN_MINUS,
TOY_TOKEN_MULTIPLY,
TOY_TOKEN_DIVIDE,
TOY_TOKEN_MODULO,
TOY_TOKEN_PLUS_ASSIGN,
TOY_TOKEN_MINUS_ASSIGN,
TOY_TOKEN_MULTIPLY_ASSIGN,
TOY_TOKEN_DIVIDE_ASSIGN,
TOY_TOKEN_MODULO_ASSIGN,
TOY_TOKEN_PLUS_PLUS,
TOY_TOKEN_MINUS_MINUS,
TOY_TOKEN_ASSIGN,
//logical operators
TOY_TOKEN_PAREN_LEFT,
TOY_TOKEN_PAREN_RIGHT,
TOY_TOKEN_BRACKET_LEFT,
TOY_TOKEN_BRACKET_RIGHT,
TOY_TOKEN_BRACE_LEFT,
TOY_TOKEN_BRACE_RIGHT,
TOY_TOKEN_NOT,
TOY_TOKEN_NOT_EQUAL,
TOY_TOKEN_EQUAL,
TOY_TOKEN_LESS,
TOY_TOKEN_GREATER,
TOY_TOKEN_LESS_EQUAL,
TOY_TOKEN_GREATER_EQUAL,
TOY_TOKEN_AND,
TOY_TOKEN_OR,
//other operators
TOY_TOKEN_QUESTION,
TOY_TOKEN_COLON,
TOY_TOKEN_SEMICOLON,
TOY_TOKEN_COMMA,
TOY_TOKEN_DOT,
TOY_TOKEN_PIPE,
TOY_TOKEN_REST,
//meta tokens
TOY_TOKEN_PASS,
TOY_TOKEN_ERROR,
TOY_TOKEN_EOF,
} Toy_TokenType;
+3 -4
View File
@@ -3,10 +3,9 @@ CC=gcc
IDIR +=. ../source ../repl
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS +=
ODIR = obj
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c)
TESTS = $(wildcard *.c)
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c) ../repl/repl_tools.c
TESTS = $(wildcard test_*.c)
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
.PRECIOUS: $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
@@ -15,7 +14,7 @@ all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
ifeq ($(shell uname),Linux)
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
valgrind --leak-check=full --track-origins=yes $@
else
$@
+11
View File
@@ -28,4 +28,15 @@ a %= 8;
assert a == 4, "%= failed";
//strings as special cases
var s = "foo";
assert s + "bar" == "foobar", "string addition failed";
assert s == "foo", "string addition failed (was too sticky)";
s += "bar";
assert s == "foobar", "string addition failed (wasn't sticky enough)";
print "All good";
+12
View File
@@ -0,0 +1,12 @@
fn fib(n : int) {
if (n < 2) {
return n;
}
return fib(n-1) + fib(n-2);
}
for (var i = 0; i < 20; i++) {
var res = fib(i);
print string i + ": " + string res;
}
-52
View File
@@ -1,52 +0,0 @@
//test basic import/export
{
var variable: int = 42;
export variable as field;
}
{
import field as value;
assert value == 42, "import/export failed";
}
//test functions using import/export
{
fn f() {
import field;
assert field == 42, "import in function failed";
}
}
//test importing/exporting of functions
{
fn func() {
return 69;
}
export func;
}
{
import func;
assert func() == 69, "import/export of functions failed";
}
//test that variables retain their types with the typeof keyword
{
var t: type = int;
export t;
}
{
import t;
assert typeof t == type, "type retention failed";
}
print "All good";
+10
View File
@@ -72,6 +72,16 @@
}
//test nested indexing multipliciation assignment
{
var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
a[1][1] *= 10;
assert a == [[1, 2, 3], [4, 50, 6], [7, 8, 9]], "nested indexing multipliciation assignment failed";
}
//test combine example
{
fn combine(a, b, c) {
+3 -5
View File
@@ -30,13 +30,11 @@
//test nested indexing
{
var d = ["foo": ["bar": 0]];
var d = ["foo" : ["bar" : ["bazz" : ["fizz" : 5]]]];
d["foo"]["bar"] = 42;
d["foo"]["bar"]["bazz"]["fizz"] = 66;
print d;
assert d == ["foo": ["bar": 42]], "nested indexing failed";
assert d == ["foo": ["bar": ["bazz": ["fizz": 66]]]], "nested indexing failed";
}
+4 -4
View File
@@ -18,7 +18,7 @@ assert greeting == "goodnight world", "basic replacement failed";
//test backwards string
assert greeting[::-1] == "dlrow thgindoog", "backwards string failed";
assert greeting[11:15:-1] == "dlrow", "backwards indexed string failed";
assert greeting[10:14:-1] == "dlrow", "backwards indexed string failed";
//test string weird manipulation
@@ -26,12 +26,12 @@ var numbers = "0123456789";
numbers[::-2] = "abc";
assert numbers == "01234c6b8a", "string weird manipulation failed";
assert numbers == "0123c5b7a9", "string weird manipulation failed";
//test indexing with variables
var first = 11;
var second = 15;
var first = 10;
var second = 14;
var third = -1;
assert greeting[first:second:third] == "dlrow", "indexing with variables failed";
+7
View File
@@ -0,0 +1,7 @@
import about as about;
import about;
assert author == "Kayne Ruse, KR Game Studios", "Author failed";
print "All good";
+280
View File
@@ -0,0 +1,280 @@
import compound;
//test concat
{
//test array concat
{
var a = [1, 2, 3];
var b = [4, 5, 6];
var c = a.concat(b).concat(b);
assert c == [1, 2, 3, 4, 5, 6, 4, 5, 6], "array.concat() failed";
}
//test dictionary concat
{
var a = ["one" : 1, "two": 2, "three": 3];
var b = ["four" : 4, "five": 5, "six": 6];
var c = a.concat(b);
assert c.length() == 6, "dictionary.concat().length() failed";
assert c == ["one" : 1, "two": 2, "three": 3, "four" : 4, "five": 5, "six": 6], "dictionary.concat() comparison failed";
}
//test dictionary concat with clashing keys
{
var a = ["one" : 1, "two": 2, "three": 3, "random": 1];
var b = ["four" : 4, "five": 5, "six": 6, "random": 2];
var c = a.concat(b);
assert c["random"] == 1, "dictionary.concat() clashing keys failed";
}
//test string concat
{
var a = "foo";
var b = "bar";
var c = a.concat(b);
assert c == "foobar", "string.concat() failed";
}
}
//test containsKey
{
var d = ["one": 1, "two": 2];
assert d.containsKey("one") == true, "dictionary.containsKey() == true failed";
assert d.containsKey("three") == false, "dictionary.containsKey() == false failed";
}
//test containsValue
{
var a = [1, 2, 3];
var d = ["one": 1, "two": 2];
assert a.containsValue(1) == true, "array.containsValue() == true failed";
assert a.containsValue(5) == false, "array.containsValue() == false failed";
assert d.containsValue(1) == true, "dictionary.containsValue() == true failed";
assert d.containsValue(3) == false, "dictionary.containsValue() == false failed";
}
//test every
{
var a = [1, 2, 3];
var d = ["one": 1, "two": 2];
var counter = 0;
fn f(k, v) {
counter++;
return v;
}
assert a.every(f) == true, "array.every() == true failed";
assert d.every(f) == true, "dictionary.every() == true failed";
assert counter == 5, "Unexpected number of calls for _every() == true";
counter = 0;
a[1] = false;
d["two"] = false;
assert a.every(f) == false, "array.every() == false failed";
assert d.every(f) == false, "dictionary.every() == false failed";
assert counter == 4, "Unexpected number of calls for _every() == false";
}
//test filter
{
var a = [1, 2, 3, 4];
var d = ["one": 1, "two": 2, "three": 3, "four": 4];
fn f(k, v) {
return v % 2 == 0;
}
assert a.filter(f) == [2, 4], "array.filter() failed";
assert d.filter(f) == ["two": 2, "four": 4], "dictionary.filter() failed";
}
//test forEach
{
var counter = 0;
fn p(k, v) {
counter++;
print string k + ": " + string v;
}
var a = ["a", "b"];
var d = ["foo": 1, "bar": 2, "bazz": 3, "fizz": 4];
a.forEach(p);
assert counter == 2, "forEach ran an unusual number of times";
counter = 0;
d.forEach(p);
assert counter == 4, "forEach ran an unusual number of times";
}
//test getKeys
{
var d = ["foo": 1, "bar": 2];
var a = d.getKeys();
assert a.length() == 2, "_getKeys() length failed";
//NOTE: dependant on hash algorithm
assert a == ["bar", "foo"], "_getKeys() result failed";
}
//test getValues
{
var d = ["foo": 1, "bar": 2];
var a = d.getValues();
assert a.length() == 2, "_getValues() length failed";
//NOTE: dependant on hash algorithm
assert a == [2, 1], "_getValues() result failed";
}
//test map
{
//test map with toy functions
{
fn increment(k, v) {
return v + 1;
}
var a = [1, 2, 3];
var d = ["four": 4, "five": 5, "six": 6];
assert a.map(increment).map(increment).map(increment) == [4,5,6], "array.map() failed";
assert d.map(increment).map(increment).map(increment) == [8,9,7], "dictionary.map() failed";
}
//test map with native functions
{
//TODO: write some native functions for use with map
}
}
//test reduce
{
var a = [1, 2, 3, 4];
var d = ["one": 1, "two": 2, "three": 3, "four": 4];
fn f(acc, k, v) {
return acc + v;
}
assert a.reduce(0, f) == 10, "array.reduce() failed";
assert d.reduce(0, f) == 10, "dictionary.reduce() failed";
}
//test some
{
var a = [false, false, false];
var d = ["one": false, "two": false];
var counter = 0;
fn f(k, v) {
counter++;
return v;
}
assert a.some(f) == false, "array.some() == false failed";
assert d.some(f) == false, "dictionary.some() == false failed";
assert counter == 5, "Unexpected number of calls for _some() == false";
counter = 0;
a[1] = true;
d["two"] = true;
assert a.some(f) == true, "array.some() == true failed";
assert d.some(f) == true, "dictionary.some() == true failed";
assert counter == 4, "Unexpected number of calls for _some() == true";
}
//test toLower
{
assert "Hello World".toLower() == "hello world", "_toLower() failed";
}
//test toString
{
var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
var s = a.toString();
assert s == "[[1,2,3],[4,5,6],[7,8,9]]", "array._toString() failed";
}
//test toUpper
{
assert "Hello World".toUpper() == "HELLO WORLD", "_toUpper() failed";
}
//test trim defaults
{
{
//test a bunch
fn test(s, pass) {
var result = s.trim();
assert result == pass, "_trim(" + result + ") failed";
}
test("hello world", "hello world");
test(" hello world", "hello world");
test("hello world ", "hello world");
test(" hello world ", "hello world");
test(" hello world", "hello world");
test("hello world ", "hello world");
test(" hello world ", "hello world");
test(" hello world", "hello world");
test("hello world ", "hello world");
test(" hello world ", "hello world");
//one for goot luck
assert " hello world ".trim() == "hello world", "hello world.trim() failed";
}
//test trim custom values
{
var chars = "heliod";
assert "hello world".trim(chars) == " wor", "custom _trim() failed";
}
//test trimBegin() & trimEnd()
assert " foo ".trimBegin() == "foo ", "string.trimBegin() failed";
assert " foo ".trimEnd() == " foo", "string.trimBegin() failed";
}
print "All good";
+40
View File
@@ -0,0 +1,40 @@
//test this logic for memory leaks
{
import compound;
import timer;
fn start(k, v) {
return startTimer();
}
fn check(k, v) {
var l = v.stopTimer();
print l.timerToString();
l.destroyTimer();
return v;
}
fn destroy(k, v) {
v.destroyTimer();
}
var arr = [1];
arr
.map(start)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(check)
.map(destroy)
;
}
print "All good";
+88
View File
@@ -0,0 +1,88 @@
import runner;
//test basic loading and freeing of a script file
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.freeScript();
}
//test basic loading and freeing of a binary file
{
var s = loadScriptBytecode("scripts:/lib/runner/sample_bytecode.tb");
s.freeScript();
}
//test running an external script
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
s.freeScript();
}
//test running an external binary file
{
var s = loadScriptBytecode("scripts:/lib/runner/sample_bytecode.tb");
s.runScript();
s.freeScript();
}
//test resetting an external script
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
s.resetScript();
assert !s.checkScriptDirty(), "checkScriptDirty failed";
s.runScript();
assert s.checkScriptDirty(), "_checkScriptDirty() failed";
s.resetScript();
s.runScript();
s.freeScript();
}
//test running a nested external script
{
var s = loadScript("scripts:/lib/runner/sample_1.toy");
s.runScript();
s.freeScript();
}
//test retrieving a script variable
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
var fib = s.getScriptVar("fib");
assert fib(12) == 144, "_getScriptVar() failed";
s.freeScript();
}
//test calling a script function
{
var s = loadScript("scripts:/runner_sample_code.toy");
s.runScript();
assert s.callScriptFn("fib", 12) == 144, "_callScriptFn() failed";
s.freeScript();
}
print "All good";
+9
View File
@@ -0,0 +1,9 @@
import runner;
//delegate to the other sample script
var s = loadScript("scripts:/lib/runner/sample_2.toy");
s.runScript();
s.freeScript();
+1
View File
@@ -0,0 +1 @@
assert true, "Nested sample scripts worked";

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