Compare commits

..

272 Commits

Author SHA1 Message Date
Kayne Ruse 6901b9a6c9 Opaque values can be referenced 2026-05-13 17:54:24 +10:00
Kayne Ruse c9d4b9965c Added function invoke in RHS values
Return also supports chained expressions.
2026-05-13 17:32:05 +10:00
Kayne Ruse ff1ef1352a Reviewed and updated tagged comments 2026-05-13 10:56:40 +10:00
Kayne Ruse 53d3606c7e Found and fixed some obscure leaks in 'Toy_Scope' 2026-05-10 17:35:07 +10:00
Kayne Ruse 83fb5222a2 Found and fixed a memory leak in the rope strings
Also fixed a bit manipulation error in the GC.
2026-05-10 15:45:16 +10:00
Kayne Ruse 3b813da1cf Zero the bucket's memory
Windows doesn't do this automatically.
2026-05-09 13:00:06 +10:00
Kayne Ruse 8b9b012bcc Fixed a memory leak 2026-05-08 17:23:36 +10:00
Kayne Ruse 6c055a0435 Implemented garbage collection
As a whole, this is still tentative.
2026-05-08 16:28:12 +10:00
Kayne Ruse be84a8dfe2 Tweaked output callbacks to match 'puts' signature 2026-05-08 11:12:27 +10:00
Kayne Ruse 185f3896c5 Fixed API naming convention
Anything prepended with TOY_API is 'public'.

Anything that starts with Toy_private_* shouldn't be touched.
2026-05-06 16:12:50 +10:00
Kayne Ruse 60a0fe8907 Added opaque attributes to API 2026-05-06 16:02:27 +10:00
Kayne Ruse 7cdb81e0bf Fixed a repl util error case 2026-05-01 20:51:32 +10:00
Kayne Ruse 18a4b33c4e Enabled the opaque value type
Also improved a couple error messages.
2026-05-01 18:42:51 +10:00
Kayne Ruse eb33775314 FIX: compounds assigned to themselves would cause self-references 2026-04-28 09:23:43 +10:00
Kayne Ruse 617a658de0 Added the preview image, so I don't lose it 2026-04-27 14:33:12 +10:00
Kayne Ruse 6ebbcc45a3 Tweaked build paths 2026-04-27 10:07:16 +10:00
Kayne Ruse b718b35097 FIX: Substrings had corner-cases with incorrect results, read more
I found this while writing unit tests for Toy_Function, where one
(native) function was named 'identity' and another (custom) was named
'ident' to avoid a naming clash. The rename didn't resolve the clash, so
after some digging, I found that strings compared to substrings would
return a match, despite being different.

This took some awkward corner-case handling, as it turns out
'deepCompareUtil' only returns zero when no differences have been found,
not when a match has been found. I also added checks for this to
Toy_String's unit test, with the parameters checked in both orders i.e.
(a,b) and (b,a), because paranoia is your friend.

The rope pattern is powerful, but also gives you enough rope to hang
yourself.
2026-04-26 22:52:24 +10:00
Kayne Ruse af30246e0c Implemented array.forEach(fn) 2026-04-26 11:59:15 +10:00
Kayne Ruse efc9fe1406 Tweaked stack allocation when inheriting VMs 2026-04-26 10:14:19 +10:00
Kayne Ruse c9a34e2259 Corrected usage of 'Toy_allocateTable' 2026-04-26 09:55:11 +10:00
Kayne Ruse 0c24a7609e Added the following attributes:
* table.length
* table.insert(key,value)
* table.hasKey(key)
* table.remove(key)

There's basically only iteration and sorting left.
2026-04-24 23:01:57 +10:00
Kayne Ruse 88e9794952 Implemented the following attributes:
* String.length
* String.asUpper
* String.asLower
* array.length
* array.pushBack(x)
* array.popBack()

The remaining attributes are listed in 'toy_attributes.h'
2026-04-24 17:33:10 +10:00
Kayne Ruse b589392b9e Comment tweaks 2026-04-24 13:01:38 +10:00
Kayne Ruse 21819b2d62 Capitalized type names 2026-04-24 12:46:27 +10:00
Kayne Ruse 4aec343b6c BUGFIX: scopes weren't expanding as intended 2026-04-24 12:46:05 +10:00
Kayne Ruse 9a75226491 Implemented the attribute operator, using a period 2026-04-24 11:31:54 +10:00
Kayne Ruse d2ac1eeb8e Syntax fix 2026-04-23 11:09:25 +10:00
Kayne Ruse f8596806ee Attempting a fix for macOS builds 2026-04-23 11:02:25 +10:00
Kayne Ruse 4957536e23 When in doubt, typecast 2026-04-23 10:45:06 +10:00
Kayne Ruse 5a867ac627 Fixed some 'size_t' issues and a bug caught on a different platform 2026-04-23 10:40:00 +10:00
Kayne Ruse 991d29e3e6 Enabled CI for other supported platforms 2026-04-23 10:10:58 +10:00
Kayne Ruse accb7f9fb4 Started work on an AST inspector 2026-04-22 15:57:55 +10:00
Kayne Ruse 63dfd33e5e Added arrays and tables to the bytecode inspector
Currently searching for an issue related to compounds.
2026-04-22 14:31:17 +10:00
Kayne Ruse 9bb115f732 Disabled bloated and verbose testing output 2026-04-22 13:06:14 +10:00
Kayne Ruse 8875b6968b Updated VM unit tests 2026-04-22 12:53:02 +10:00
Kayne Ruse 97d16c1184 Updated compiler unit tests 2026-04-22 12:07:40 +10:00
Kayne Ruse 47c5d49069 Updated parser unit test, fixed a missing stack pop for binary exprStmt 2026-04-22 11:40:53 +10:00
Kayne Ruse 2c92f829e1 Fixed stack overflow caused by expression statements
This is a longstanding bug, so I'm glad its fixed, even if its only a
bandaid.

This does break some tests, but I'm too tired and these tests are out of
date.
2026-04-17 23:43:30 +10:00
Kayne Ruse 5b101d763e Expanded bytecode inspector, added functions to Toy_copyValue 2026-04-17 11:55:46 +10:00
Kayne Ruse 81c95ff69d Cleared out some unneeded notes 2026-04-17 09:25:18 +10:00
Kayne Ruse 88100b128a Implemented native C functions called from Toy scripts
There's only example functions for now, but I'll add type-specific
functions later.
2026-04-16 21:14:42 +10:00
Kayne Ruse 3a0f11ebb4 Added string type check 2026-04-16 19:30:23 +10:00
Kayne Ruse f9790b99ce Moved type coersion check, functions can be compared
Also updated some tagged comments
2026-04-15 15:04:54 +10:00
Kayne Ruse dde52f9d8a Workflow tinkering 2026-04-15 14:17:32 +10:00
Kayne Ruse cda4bee6ee Double-checked strncpy doesn't have bugs
Apparently the linux kernel removed strncpy entirely.
2026-04-13 12:09:09 +10:00
Kayne Ruse 9a10fadada Tweaked CI 2026-04-12 23:15:56 +10:00
Kayne Ruse 8eefbc8a0c Added remaining opcodes to inspector
Also used some coloring for terminal outputs
2026-04-12 23:04:31 +10:00
Kayne Ruse e24823924a Comment tweaks 2026-04-12 15:02:07 +10:00
Kayne Ruse c0c03a4110 Functions are working, tests incomplete
This required a massive cross-cutting rework to the scope system,
multiple subtle bugfixes and relearning of the parser internals, but it
does appear that functions are working correctly.

A few caveats: for now, parameters are always constant, regardless of
type, return values can't be specified, and some script tests have been
written.

Most importantly, a key feature is working: closures.
2026-04-12 11:52:58 +10:00
Kayne Ruse b0d9c15d33 Added a benchmark for JS 2026-04-12 08:55:15 +10:00
Kayne Ruse 24e5d8081c Tweaked a doc 2026-04-12 08:42:06 +10:00
Kayne Ruse 42aef306a9 Added a lua benchmark for performance comparisons 2026-04-12 08:40:28 +10:00
Kayne Ruse 6f27d07829 Added a few opcodes to the inspector 2026-04-12 00:19:20 +10:00
Kayne Ruse b32ea9f309 Started working on a decompiler, called 'bytecode inspector'
It only has a few instructions for now, but I can flesh it out over
time.
2026-04-11 13:37:26 +10:00
Kayne Ruse 49a825aaf9 Removed some old notes 2026-04-11 02:06:59 +10:00
Kayne Ruse baa81b1aa9 Removed annoying workingdir thing 2026-04-11 01:55:11 +10:00
Kayne Ruse 9553edef9c Added tools for vscode 2026-04-11 01:48:30 +10:00
Kayne Ruse 66155fa213 Tweak, but I need a decompiler now 2026-04-10 16:19:03 +10:00
Kayne Ruse 547229e150 Script tests re-added, all tests can run under gdb
Also fixed a minor bug with printing, and removed the ability to
configure the parser.

Added and updated QUICKSTART.md as a quick way to get people started.

There's some broken scripts under 'scripts/' that require functions to
work properly.
2026-04-10 15:28:56 +10:00
Kayne Ruse 211744535e Tweak 2026-04-10 12:15:02 +10:00
Kayne Ruse 3a24fbf6e1 Tweaked CI 2026-04-07 22:00:54 +10:00
Kayne Ruse 842f041a50 VM test is passing 2026-04-07 21:34:05 +10:00
Kayne Ruse 7408a24a12 Scope test is working 2026-04-07 20:48:32 +10:00
Kayne Ruse 09fc6d5279 Compiler test is passing 2026-04-07 20:36:58 +10:00
Kayne Ruse f25e81cd09 Parser test is passing 2026-04-07 20:14:27 +10:00
Kayne Ruse 48072f0dd1 AST test is passing 2026-04-07 20:06:44 +10:00
Kayne Ruse 522fc3e64b Value test is passing 2026-04-07 19:44:28 +10:00
Kayne Ruse f4ce6ad9f1 String test is passing
Note: String fragmentation is no longer supported
2026-04-07 19:22:31 +10:00
Kayne Ruse 3f35502694 Updatedd license 2026-04-07 12:30:25 +10:00
Kayne Ruse f06218b9cd 'print' now works as expected
String literals are now stored in the bucket mid-compilation, but this
is fine, as the buckets are freed one the bytecode is complete.
2026-04-06 23:08:17 +10:00
Kayne Ruse 1ae3fcbf73 WIP: Scopes weren't tracking their content sizes
'print' no longer segfaults from a long chain of indirect memory frees.

It still doesn't work though, which is odd.
2026-04-06 21:50:41 +10:00
Kayne Ruse abae97b6e5 Fixed workflow 2026-04-05 18:48:27 +10:00
Kayne Ruse fbb7e1bc54 WIP: Retreived the unit tests (formerly test cases)
Some of these still work, others have just been dummied out for now.

Also added tests for console colors tool, and tweaked it to work
properly.
2026-04-05 18:42:56 +10:00
Kayne Ruse 57fe9bb00d WIP: Compiles but still very broken 2026-04-05 17:04:30 +10:00
Kayne Ruse 914ee6fcfa WIP: Fixed strings and scopes, still reworking impacted areas 2026-04-05 10:13:58 +10:00
Kayne Ruse ba9418f365 Removed unneeded files and console colors are disabled 2026-04-04 20:35:12 +11:00
Kayne Ruse 98208f4bb5 Began cleaning up this project for a soft reboot 2026-04-04 19:32:45 +11:00
Kayne Ruse 5a0012d73a Tweaked README
...because I'm still not feeling like coding, thanks to this damn flu.
2025-03-01 12:30:53 +11:00
Kayne Ruse 24d111e52d Updated links after moving the repo 2025-02-28 20:12:00 +11:00
Kayne Ruse 4b21e61df0 Updated README 2025-02-28 11:30:01 +11:00
Kayne Ruse d3b59eb0da WIP functions working, untested, memory issues, read more
I've ripped out the deep copying, and flattened the bucket usage, but
this results in no memory being freed or reused for the lifetime of the
program.

This is shown most clearly with this script:

```toy
fn makeCounter() {
	var counter: int = 0;

	fn increment() {
		return ++counter;
	}

	return increment;
}

var tally = makeCounter();

while (true) {
	var result = tally();

	if (result >= 10_000_000) {
		break;
	}
}
```

The number of calls vs amount of memory consumed is:

```
function calls -> memory used in megabytes
1_000 -> 0.138128
10_000 -> 2.235536
100_000 -> 21.7021
1_000_000 -> 216.1712
10_000_000 -> 1520.823
```

Obviously this needs to be fixed, as ballooning to gigabytes of memory
in only a moment isn't practical. That will be the next task - to find
some way to free memory that isn't needed anymore.

See #163, #160
2025-02-21 11:41:27 +11:00
Kayne Ruse 9fe6d6b218 WIP bad approach, read more
I build a self-referential system, then tried to copy only parts. I need
to step back and adjust my approach.

'Toy_private_deepCopyValue' and 'Toy_private_deepCopyScope' need to be
ripped out, and I need to simply accept there will be only one instance
of 'Toy_Bucket' that isn't freed until the top-level VM is.

I need an hour's break before I'll tackle this again.

See #163
2025-02-18 13:06:15 +11:00
Kayne Ruse 3a82593e4d Adjusted timetable 2025-02-18 08:34:29 +11:00
Kayne Ruse 639250f028 WIP return keyword, read more
Functions are having issues with being copied around, especially
between buckets, leading to the scopes getting looped. The program gets
stuck in 'incrementRefCount()'.

It's past my time limit, so I'll keep working on it tomorrow with a
fresh mind.

All function stuff is still untested.

See #163
2025-02-17 19:10:24 +11:00
Kayne Ruse 02dfc996b4 Functions are successfully called, read more
Return keyword is not yet implemented.

Functions are untested.

See #163
2025-02-17 14:05:07 +11:00
Kayne Ruse 76d89fe0ad Reduced excessive calls to Toy_unwrapValue()
In practice, references only point to arrays or tables.

Fixed #176
2025-02-17 04:42:22 +11:00
Kayne Ruse 15aea7c032 Tweaked Timetable, read more
I'm only a couple hours short of my goal, but I'm taking the weekend off
to relax, and this'll be ready next week.
2025-02-13 18:28:20 +11:00
Kayne Ruse 574502bf3e Removed old contribs file 2025-02-11 14:28:20 +11:00
Kayne Ruse 258968d7a4 WIP, Functions are declared, still not called 2025-02-11 11:55:35 +11:00
Kayne Ruse 7a8c415b3f Functions are written, but not yet executed, untested 2025-02-10 20:17:53 +11:00
Kayne Ruse 344c265918 Parameters are immutable
It took me a whole day to change one line.
2025-02-09 14:34:44 +11:00
Kayne Ruse f2660b95eb Credited @NishiOwO for NetBSD support 2025-02-09 00:40:21 +11:00
Kayne Ruse fe64e876bd Merge pull request #175 from NishiOwO/v2
Compile on NetBSD
2025-02-09 00:32:00 +11:00
NishiOwO 1006b6e216 Add bitness definitions, and platform definitions 2025-02-08 22:14:32 +09:00
NishiOwO a1cfc095a7 Compile on NetBSD 2025-02-08 22:09:28 +09:00
Kayne Ruse 7c054db9e6 Compiler now reuses existing strings in the data, read more
If a string exists in the data, instead of being written, the function
'emitCStringToData()' will instead return the address of the match
within the data section.

Then, I can search the jump table for that address, and use the existing
jump entry or append a new one.

Fixes #168
2025-02-08 17:27:47 +11:00
Kayne Ruse 72f4e4c143 Tweaked workflow to fix GH's new runners 2025-02-08 12:16:42 +11:00
Kayne Ruse e2a284d9ce Ensured parser is reading functions correctly, read more
Parameter lists are read manually, rather than delegating to
parsePrecedence, because that was causing issues.

function bodies are read as block-level statements, just like the entire
files.
2025-02-08 12:00:55 +11:00
Kayne Ruse c646904407 replaced void* with unsigned char* everywhere 2025-02-08 00:53:23 +11:00
Kayne Ruse 470836a390 Renamed Toy_ModuleBuilder to Toy_ModuleCompiler
I also noticed that Toy_ModuleBundle isn't being used right now.
2025-02-08 00:23:19 +11:00
Kayne Ruse 212eca1825 Tweaked README 2025-02-06 11:25:53 +11:00
Kayne Ruse b93ea5006c Functions are WIP, read more
They're no-ops in compilation for now, and param types aren't parsed.

'return' keyword needs to be implemented.

Was distracted by bugfixes in v2 and v2-docs.
2025-02-02 17:26:47 +11:00
Kayne Ruse 336616a1bf Tweaked truthiness, fixed int to float coersion 2025-02-02 16:38:46 +11:00
Kayne Ruse 63cc530899 Fixed continue keyword, was pointing at break's target 2025-02-02 16:15:20 +11:00
Kayne Ruse 3c0a50c4cd Tweaked AST bitness test 2025-02-01 10:04:19 +11:00
Kayne Ruse 481d17f040 Tests are passing, added preserveScope to VM API 2025-01-31 13:51:53 +11:00
Kayne Ruse bfed4e23f3 Repl works, also fixed a few small bugs 2025-01-31 10:16:06 +11:00
Kayne Ruse 002651f95d WIP, adjusting architecture, read more
The 'source' directory compiles, but the repl and tests are almost
untouched so far. There's no guarantee that the code in 'source' is
correct, so I'm branching this for a short time, until I'm confident the
whole project passes the CI again.

I'm adjusting the concepts of routines and bytecode to make them more
consistent, and tweaking the VM so it loads from an instance of
'Toy_Module'.

* 'Toy_ModuleBuilder' (formally 'Toy_Routine')

This is where the AST is compiled, producing a chunk of memory that can
be read by the VM. This will eventually operate on individual
user-defined functions as well.

* 'Toy_ModuleBundle' (formally 'Toy_Bytecode')

This collects one or more otherwise unrelated modules into one chunk of
memory, stored in sequence. It is also preprended with the version data for
Toy's reference implementation:

For each byte in the bytecode:

    0th: TOY_VERSION_MAJOR
    1st: TOY_VERSION_MINOR
    2nd: TOY_VERSION_PATCH
    3rd: (the number of modules in the bundle)
    4th and onwards: TOY_VERSION_BUILD

TOY_VERSION_BUILD has always been a null terminated C-string, but from
here on, it begins at the word-alignment, and continues until the first
word-alignment after the null terminator.

As for the 3rd byte listed, since having more than 256 modules in one
bundle seems unlikely, I'm storing the count here, as it was otherwise
unused. This is a bit janky, but it works for now.

* 'Toy_Module'

This new structure represents a single complete unit of operation, such
as a single source file, or a user-defined function. It is divided into
three main sections, with various sub-sections.

    HEADER (all members are unsigned ints):
        total module size in bytes
        jumps count
        param count
        data count
        subs count
        code addr
        jumps addr (if jumps count > 0)
        param addr (if param count > 0)
        data addr (if data count > 0)
        subs addr (if subs count > 0)
    BODY:
        <raw opcodes, etc.>
    DATA:
        jumps table
            uint array, pointing to addresses in 'data' or 'subs'
        param table
            uint array, pointing to addresses in 'data'
        data
            heterogeneous data, including strings
        subs
            an array of modules, using recursive logic

The reference implementation as a whole uses a lot of recursion, so this
makes sense.

The goal of this rework is so 'Toy_Module' can be added as a member of
'Toy_Value', as a simple and logical way to handle functions. I'll
probably use the union pattern, similarly to Toy_String, so functions
can be written in C and Toy, and used without needing to worry which is
which.
2025-01-29 10:21:33 +11:00
Kayne Ruse a1f6f147c5 Tweaked timetable 2025-01-29 10:16:55 +11:00
Kayne Ruse f9715c2016 Too buggered to think today. 2025-01-15 14:05:31 +11:00
Kayne Ruse 6c293cfc62 tweaked README 2025-01-15 08:59:40 +11:00
Kayne Ruse 05451af8d7 What is the most bullshit error you've ever seen?
'error: no newline at end of file'

This only occurs in the MacOS builds.
2025-01-15 08:40:22 +11:00
Kayne Ruse 730353342d Updated license date, tweaked an error message 2025-01-14 13:23:23 +11:00
Kayne Ruse 513d8f130c Fixed obscure EOF bug in lexer 2025-01-14 12:45:11 +11:00
Kayne Ruse 13daf95933 Added docs link 2025-01-12 16:26:24 +11:00
Kayne Ruse 9141102f2e Fixed stack overflow caused by assignments, read more
Variable declaration was also causing this issue.

It was caused by a value being left on the stack after these statements.
It wasn't a quick fix, as chained assignments depended on it. Now, the
assignment opcode has a configuration option, indicating if the last
value should be left on the stack or not.

This also means the benchmark in 'scripts/benchpress.toy' will no longer
cause a stack overflow.

Fixed #171
2025-01-11 15:07:29 +11:00
Kayne Ruse ce03a342c9 Fixed a missing ';' required at the end of var statements 2025-01-11 13:27:06 +11:00
Kayne Ruse c6d8766bc3 Added scripts/benchpress.toy
This file works in Toy v1 and Toy v2, but currently causes an SO.

See #171
2025-01-10 04:08:10 +11:00
Kayne Ruse 14696833fd Postfix '++' & '--' works (prefix & postfix are both tested) 2025-01-09 18:33:10 +11:00
Kayne Ruse 6f16c31f24 Prefix '++' working (postfix is next) 2025-01-09 16:45:48 +11:00
Kayne Ruse 3aee2ba664 Added quotemarks to internal string elements
This applies to strings within arrays and tables.
2025-01-09 12:11:23 +11:00
Kayne Ruse b55192e513 Removed stubs for types as values
Also potentially fixed a bug in the previous commit, not certain
2025-01-09 11:46:29 +11:00
Kayne Ruse 90ffe9b40e Disallow empty bodies in control flow statements, added 'pass' keyword
Fixed #170
2025-01-09 11:03:03 +11:00
Kayne Ruse 9de9c85bea Update README.md 2025-01-01 21:21:14 +11:00
Kayne Ruse 23eb3e45df Keywords 'break' & 'continue' tested
There were a couple bugs - I'm glad I'm so thorough with these tests.

See #152
2024-12-30 16:56:57 +11:00
Kayne Ruse b84a70cc34 Keywords 'break' and 'continue' are working, untested
See #152
2024-12-30 09:50:51 +11:00
Kayne Ruse 6d25beea03 Removed minPsl from tables, not needed (always zero)
Closes #166
2024-12-27 17:24:28 +11:00
Kayne Ruse e96f87ff45 Merge remote-tracking branch 'refs/remotes/origin/v2' into v2 2024-12-27 13:07:19 +11:00
Kayne Ruse 0192b60338 Updated README.md, filled out issue tracker, planning ahead 2024-12-27 13:04:44 +11:00
Kayne Ruse c3a737eae5 Added a reference file to notes 2024-12-26 17:13:46 +11:00
Kayne Ruse cc4ff3f6d8 Fixed logical AND and OR operators, read more
The definition of '&&':
  Return the first falsy value, or the last value, skipping the evaluation of other operands.

The definition of '||':
  Return the first truthy value, or the last value, skipping the evaluation of other operands.

Toy now follows these definitions.

Fixed #154
2024-12-26 16:09:16 +11:00
Kayne Ruse 153ab54be6 Fixed some incorrect precedence rules 2024-12-26 14:46:12 +11:00
Kayne Ruse 24cfe7f539 Fixed some error states and error messages, read more
By leaving 'null' on the stack, it won't cause stack underflows in a
bunch of erroneous situations. This will allow the repl (and other
situations) to continue if they want to.

I've also fixed some error messages in toy_table.c, which were formatted
badly.

Closes #162
2024-12-26 14:33:26 +11:00
Kayne Ruse 3ca816439e Swapped some if-checks for asserts, closes #161
Thanks 'devast8a' on discord
2024-12-26 13:09:21 +11:00
Kayne Ruse 9cb138a7d6 Added tables to integration tests, tweaked a lot of comments 2024-12-25 11:04:18 +11:00
Kayne Ruse 9e2cbb1f59 Quick fix for some self-referential table bugs 2024-12-24 16:47:58 +11:00
Kayne Ruse b092b8ce50 Added tables to the scripts, untested
This also has a lot of bugfixing.
2024-12-24 16:08:42 +11:00
Kayne Ruse 4faa0c0476 Fixed nested assignment bug 2024-12-24 11:58:51 +11:00
Kayne Ruse 223db840c8 Benchmarked memory models for Toy_Array, read more
The results can be found in
'tests/benchmarks/array_allocation/results.md'

The results are disappointing, as 'malloc()' is simply faster in every
possible situation compared to my custom arena allocator.
2024-12-24 10:37:54 +11:00
Kayne Ruse 8b5cc3b493 Wrote a benchmark to test TOY_BUCKET_IDEAL sizes 2024-12-18 00:40:17 +11:00
Kayne Ruse 04c799954c Tweaked standard bucket sizes, see #160
Hyacinth: It's pronounced "Bouquet"!
2024-12-17 22:25:33 +11:00
Kayne Ruse 3e17916a4a Prepped for #160, fixed a stack-shrink bug 2024-12-17 21:18:45 +11:00
Kayne Ruse a28053d4e9 Reworked Toy_String as a union, enabled -Wpedantic
Toy now fits into the C spec.

Fixed #158

Addendum: MacOS test caught an error:

error: a function declaration without a prototype is deprecated in all versions of C

That took 3 attempts to fix correctly.

Addendum: 'No new line at the end of file' are you shitting me?
2024-12-15 15:52:06 +11:00
Kayne Ruse 93fce94e9c Locales are hard, sorry!
Fixed #159
2024-12-14 12:57:53 +11:00
Kayne Ruse 7be63c8ccc Added -Wpointer-arith to CFLAGS, read more
I attempted to add '-Wpedantic' to CFLAGS, but it seems that my usage of
the variable length arrays within unions is causing an error that can't
be selectively disabled:

error: invalid use of structure with flexible array member [-Werror=pedantic]

This is the offending code: /source/toy_string.h#L9-L37

It seems that tagged unions, with VLAs within, is simply not allowed.
Unfortunately, my whole string system depends on it. I'll have to find some way
around it.

I've also updated the debugging output in repl/main.c.
2024-12-12 18:23:44 +11:00
Kayne Ruse cf9affe190 Fixed unused param
Accidentally used a c23 feature.

MacOS runner didn't like that.
2024-12-12 16:23:09 +11:00
Kayne Ruse fce71a6cda Tweaked CFLAGS, and fixed related errors 2024-12-12 16:18:41 +11:00
Kayne Ruse 5f4dfdccc5 Updated docs and comments 2024-12-11 17:07:49 +11:00
Kayne Ruse 476a79b746 Updated README.md syntax examples 2024-12-10 17:10:09 +11:00
Kayne Ruse 5f75b5f1a3 Allowed for empty arrays and trailing commas 2024-12-10 10:44:13 +11:00
Kayne Ruse 1a36c14247 Expanded array tests, read more
Getting the array's length is still not available yet, so I'm not
marking arrays as done - but everything that is there is tested.

I've also tweaked the assert output callbacks to also print 'assert failure'.
2024-12-09 12:11:31 +11:00
Kayne Ruse 61a105db2d Compound assignment for arrays is working, untested, read more
I added reference values in 62ca7a1fb7,
but forgot to mention it. I'm now using references to assign to the
internals of an array, no matter how many levels deep it is.
2024-12-07 15:35:06 +11:00
Kayne Ruse 2324150fd5 Update CODE_OF_CONDUCT.md 2024-12-07 07:43:28 +11:00
Kayne Ruse 03dce296cb Assignment target is now an AST node
This will make assigning to arbitrary targets easier.
2024-12-04 19:24:58 +11:00
Kayne Ruse 62ca7a1fb7 WIP: Implementing arrays into the script, read more
I had intended to solve the Advent of Code puzzles in Toy, but they
don't work without arrays. I wasn't able to enable arrays in time, so
I need to focus on doing things correctly.

The most immediate tasks are marked with 'URGENT', and a lot of tests
need writing.
2024-12-02 11:58:03 +11:00
Kayne Ruse 12c6ac938c WIP: tests incomplete 2024-11-30 12:50:46 +11:00
Kayne Ruse 58cecafb3b WIP: Adding arrays to value structure, tests incomplete 2024-11-29 12:17:54 +11:00
Kayne Ruse bb2e85e350 Updated README.md 2024-11-29 12:16:10 +11:00
Kayne Ruse 431893bf60 WIP: Started on break & continue, needs Toy_Array in Toy_Value first 2024-11-28 13:43:26 +11:00
Kayne Ruse 6cc331d462 While is working but untested, read more
* TODO: break and continue keywords need to be implemented
* TODO: while-then needs testing
* Fixed the parser not liking zero-length strings
2024-11-26 14:48:24 +11:00
Kayne Ruse 0947430c29 Finished if-then-else tests, finally. 2024-11-26 11:30:28 +11:00
Kayne Ruse 1695f9d1c9 Found and fixed a bug in the parser 2024-11-23 15:43:31 +11:00
Kayne Ruse 79c4374a1f Make sure this test works without scope braces too 2024-11-23 13:41:02 +11:00
Kayne Ruse 37fb3927a6 Implemented some tests, not finished yet
* parser compounds
* routine keyword assert
2024-11-23 10:45:19 +11:00
Kayne Ruse 0b559ecb74 Fixed the lines marked as 'URGENT' 2024-11-23 10:07:05 +11:00
Kayne Ruse 7d4ea4881f WIP: Fixed print bug, tests incomplete, read more
I was sidetracked by a strange display bug - turns out it was caused by
pointers - this commit fixes it.

The tests for if-then-else still aren't finished, but I'm knocking off
as it's past my time limit. I've marked 'TODO' and 'URGENT' using
comments, so finding the issues should be easy.
2024-11-22 18:21:47 +11:00
Kayne Ruse b29a87840d EMERGENCY: Computer is dying, this is an incomplete commit, do not use 2024-11-22 15:48:44 +11:00
Kayne Ruse b2b2ca7e53 If-then-else is working, untested 2024-11-20 12:50:27 +11:00
Kayne Ruse 34577ecfe1 WIP: if-then-else, incomplete 2024-11-19 17:14:39 +11:00
Kayne Ruse 7398898a61 Added valgrind to the CI, fixed tests
This exposed an issue with my dev environment, which I had to patch.

Fixed #153
2024-11-17 18:49:40 +11:00
Kayne Ruse 2f9489d5fd Fixed a 'malformed assignment' issue, read more
I've also added some support for compiler errors in general, but these
will get expanded on later.

I've also quickly added a valgrind option to the tests and found a few
leaks. I'll deal with these later.

Summary of changes:

* Clarified the lifetime of the bytecode in memory
* Erroneous routines exit without compiling
* Empty VMs don't run
* Added a check for malformed assignments
* Renamed "routine" to "module" within the VM
* VM no longer tries to free the bytecode - must be done manually
* Started experimenting with valgrind, not yet ready
2024-11-16 21:02:37 +11:00
Kayne Ruse 04f0653595 Planned control flow statements 2024-11-16 17:57:51 +11:00
Kayne Ruse cd2113594d Fixed a call when using print keyword 2024-11-12 22:23:34 +11:00
Kayne Ruse be7e4ddd18 Reworked the tests, read more
I've brought the tests up to scratch, except for compounds im the
parser, because I'm too damn tired to do that over SSH. It looks like
collections are right-recursive, whixh was unintended but still works
just fine.

I've also added the '--verbose' flag to the repl to control the
debugging output.

Several obscure bugs have been fixed, and comments have been tweaked.

Mustfail tests are still needed, but that's a low priority. See #142.

Fixed #151
2024-11-12 22:04:07 +11:00
Kayne Ruse b74aa63c1c Added verbose debugging option to the REPL 2024-11-12 11:16:50 +11:00
Kayne Ruse 935993ee8a Adding discord webhook 2024-11-11 19:17:07 +11:00
Kayne Ruse 436bd3ffca Added silent cmd args
Fixed #145
2024-11-10 10:27:50 +11:00
Kayne Ruse 1608a13b43 Implemented assert keyword, read more
The assert keyword works, but I want to add a cmd option to suppress or
disable the errors.

The tests need some serious TLC. I know that, but I'm kicking it down
the road for now.
2024-11-09 17:41:29 +11:00
Kayne Ruse 1925d41940 Added indexing to strings, tests still needed 2024-11-09 13:10:54 +11:00
Kayne Ruse 5588986042 Tweaked file locations
Also altered the commit message, because there was a weird character in
it.
2024-11-03 12:10:39 +11:00
Kayne Ruse 32727f986c Benchmarked and tweaked Toy_Table, read more
I read the DOOM 1 source code and found a neat trick to replace modulo.

YOINK!!!
2024-11-02 21:29:13 +11:00
Kayne Ruse 92955d56dd Added a nice error message 2024-11-02 14:39:59 +11:00
Kayne Ruse ef72415c21 Updated README 2024-11-02 11:52:54 +11:00
Kayne Ruse 7173f7770f Added types and constness
Fixed #144
2024-11-02 11:39:47 +11:00
Kayne Ruse 3ad53c5e0e Tweaked README 2024-10-31 14:54:46 +11:00
Kayne Ruse 3cb2132bfa Escaped characters stored in strings correctly
Fixed #147
2024-10-30 21:13:57 +11:00
Kayne Ruse feab02e790 Fixed a test's return value
Only one platform caught this?
2024-10-30 21:00:02 +11:00
Kayne Ruse 163c8aa7f6 Fixed a printf format issue 2024-10-30 20:52:52 +11:00
Kayne Ruse d19ca1bcee Reworked variable equality and comparisons
Fixed #146
2024-10-30 19:58:55 +11:00
Kayne Ruse b30a092ab8 Added more reserved words 2024-10-27 18:07:52 +11:00
Kayne Ruse e3bb7c0d25 Renamed a macro 2024-10-27 14:19:28 +11:00
Kayne Ruse c5206daaea Implemented scopes 2024-10-27 13:44:09 +11:00
Kayne Ruse d22b18ed17 Variable access is working 2024-10-26 10:35:47 +11:00
Kayne Ruse 2ee19c7c66 Automatically free container elements if needed 2024-10-26 09:45:22 +11:00
Kayne Ruse 3148a56ce0 Added simple assignment, read more
I was coding earlier this week, but my brain was so foggy I ended up not
knowing what I was doing. After a few days break, I've cleaned up the
mess, which took hours.

Changes:
* Variables can be assigned
* Added new value types as placeholders
* Added 'compare' and 'assign' to the AST
* Added duplicate opcode
* Added functions to copy and free values
* Max name length is 255 chars
* Compound assigns are squeezed into one word

To be completed:

* Tests for this commit's changes
* Compound assignments
* Variable access
2024-10-25 22:48:24 +11:00
Kayne Ruse 5b17c5e1e9 Comment tweak 2024-10-19 21:14:19 +11:00
Kayne Ruse ddd91e389e Tweaked README blurb 2024-10-19 20:20:17 +11:00
Kayne Ruse 5d37d06343 Benchmarks are working, but empty
Lots of work and stress for a tint bit of progress.

See #131
2024-10-19 15:58:17 +11:00
Kayne Ruse 787a1cca84 Fixed table tests, ready for some benchmarking, see #131 2024-10-19 10:13:22 +11:00
Kayne Ruse 98ea0e5884 Reworking structures for easy testing, read more
I was reworking bits of the containers to address issue #131, then
realized that the table's tests depended on a specific initial size. I'm
too buggered to finish it tonight, so I'll fix it tomorrow.
2024-10-18 20:48:33 +11:00
Kayne Ruse c3ee92fcef Arrays now store Toy_Values as elements
Fixed #136
2024-10-18 13:06:42 +11:00
Kayne Ruse 09e4cb7b03 Squashed: Added integration and standalone tests, read more
This wasn't an easy fix, as it was primarily the test pipelines that
were failing. I resorted to using forced pushes to run the CI, to try
and track down the problems.

The primary cause seems to be the differences in how each supported
platform handles file paths, specifically, slash vs. backslash.

I've also added gdb scripts to set up automated breakpoints, and to run
operations on them to check for issues - the 'gdb_init' files are mostly
empty for the time being.

commit a34b0ff5d407bbe7d70ff9504aa035ec6fbecb7c
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Oct 18 11:45:40 2024 +1100

    Restored the workflows

commit eb3d94f30d4dc4150139517f44cc874f2901124f
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Oct 18 11:35:39 2024 +1100

    I think the library path on macos is fixed

commit 964572b5e93c7cb464686f19ddbe3e9d315f391b
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Oct 18 11:22:56 2024 +1100

    I think the file paths are fixed

commit 1721f3da7252b4063f4347926e800ef4f7c9bf4c
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Thu Oct 17 15:57:28 2024 +1100

    Added standalone tests

commit 90c783f4059d88f4a7bbaf18215a9b414f3ab66f
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Thu Oct 17 15:01:18 2024 +1100

    Trying to fix the integration test pipeline

commit fccced1396568a55c1385e2f1b04fedf7c2585a5
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Wed Oct 16 00:31:39 2024 +1100

    Workflow integration tests are not passing

commit 6b1e0d1e0f89291e89768bf6102f4f7ed4581496
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Tue Oct 15 20:07:20 2024 +1100

    Fixed file paths in workflow

commit c0f1ec78fe79a5abb34c3e05308236cb18c23b97
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Tue Oct 15 19:46:14 2024 +1100

    Moved example scripts into proper integration tests

    Also adjusted makefiles to allow easy invoking of the tests.

    Adjusted and updated CI to invoke tests correctly.

    Fixed #141
2024-10-18 12:04:29 +11:00
Kayne Ruse 425ef7e3e0 Moved keywords into the lexer 2024-10-14 12:41:53 +11:00
Kayne Ruse 694e262ee2 Tweaked 4-byte alignment function
Fixed #140

Thanks @8051enthusiast
2024-10-14 02:07:24 +11:00
Kayne Ruse a51c5591ee Fixed declarations without initial value 2024-10-13 15:18:48 +11:00
Kayne Ruse 80734563b9 Implemented and tested variable declaration
Assignment, etc. is still to come, as are types.
2024-10-13 15:02:42 +11:00
Kayne Ruse 1ad6bdff70 Expanded defaults for bucket sizes 2024-10-12 23:02:15 +11:00
Kayne Ruse bfc8fe3717 Tweaked CFLAGS in each makefile 2024-10-12 21:47:03 +11:00
Kayne Ruse 7b1dbf25ff Toy_String now fragments strings that are too long 2024-10-12 20:25:41 +11:00
Kayne Ruse c1d72adb71 Finished the scope tests 2024-10-12 19:34:37 +11:00
Kayne Ruse 87793b694a Wrote a bit of the scope tests, they're still incomplete 2024-10-11 16:29:15 +11:00
Kayne Ruse 8d1e4d647b Started working on Toy_Scope, incomplete
I only worked for a couple hours today.
2024-10-10 22:53:49 +11:00
Kayne Ruse e6fa345fe6 Cleaned up some comments 2024-10-10 09:10:39 +11:00
Kayne Ruse ca4073a5ae Removed a couple of small optimisations form the parser 2024-10-10 08:18:27 +11:00
Kayne Ruse 9f45925072 Fixed empty inputs breaking the interactive repl
Also tweaked README.md and CONTRIBUTING.md
2024-10-08 23:33:54 +11:00
Kayne Ruse 0779798347 Fixed the null character not being copied 2024-10-08 15:24:47 +11:00
Kayne Ruse 93f771dc8d Added interactive loop to the repl, read more
Other changes include:

* Added Toy_initVM(), to allow Toy_resetVM() to retain memory
* Added tests that reset and reuse the same VM
2024-10-08 14:59:24 +11:00
Kayne Ruse 4bcf8e84a9 String literals are being parsed, compiled and printed, read more
Strings, due to their potentially large size, are stored outside of a
routine's code section, in the data section. To access the correct
string, you must read the jump index, then the real address from the
jump table - and extra layer of indirection will result in more flexible
data down the road, I hope.

Other changes include:

* Added string concat operator ..
* Added TOY_STRING_MAX_LENGTH
* Strings can't be created or concatenated longer than the max length
* The parser will display a warning if the bucket is too small for a
  string at max length, but it will continue
* Added TOY_BUCKET_IDEAL to correspend with max string length
* The bucket now allocates an address that is 4-byte aligned
* Fixed missing entries in the parser rule table
* Corrected some failing TOY_BITNESS tests
2024-10-08 00:33:36 +11:00
Kayne Ruse 14653a303f Added 'Toy_String' to 'Toy_Value' structure, read more
To help with storing strings within tables, I've replaced the unused
'_padding' member of 'Toy_String' with 'cachedHash', which is set to
zero on string allocation.

The hash of a string isn't generated and stored until it's actually
needed, as the rope pattern means not every string needs a hash -
hopefully this will save unnecessarily wasted time.

When a hash of a string is needed, the hashing function first checks to
see if that string already has one, and if so, returns it. Again, less
time wasted.

When generating a new string hash, the hashing function takes the
string's type into account, as node-based strings first need their
contents assembled into a simple char buffer.

Other changes include:

* Changed 'TOY_VALUE_TO_*' to 'TOY_VALUE_FROM_*'
* Changed 'TOY_VALUE_IS_EQUAL' to 'TOY_VALUES_ARE_EQUAL'
* Added a missing '#pragma once' to 'toy_print.h'
2024-10-07 17:35:31 +11:00
Kayne Ruse d62ee2a9a3 Added 'name' to string types 2024-10-07 14:13:39 +11:00
Kayne Ruse ff13b5cf38 Implemented print keyword and associated tests 2024-10-07 12:13:06 +11:00
Kayne Ruse 956ebbeb28 Implemented Toy_compareStrings() 2024-10-06 16:09:00 +11:00
Kayne Ruse 4805c6757a Added terminal print callbacks
Resolved #127
2024-10-05 23:29:24 +10:00
Kayne Ruse ad44eeac48 Removed bytecodeSize parameter 2024-10-05 19:44:46 +10:00
Kayne Ruse d19a90f9bd Added CONTRIBUTING.md
Also renamed 'handles' throughout the project for consistency.

Some meta files also had their file extensions added.
2024-10-05 12:17:27 +10:00
Kayne Ruse 29f5647e31 Small tweak, read more
A table with 400 elements has a maximum PSL of 4.

This is only under test conditions, but WOW.
2024-10-04 17:10:23 +10:00
Kayne Ruse 196b5f86f1 Wrote tests for Toy_Table
I hope that's it, I don't wanna do that again XD
2024-10-04 16:50:54 +10:00
Kayne Ruse 5cf2e70b7d Implemented 'Toy_Table' hashtable with robin hood algorithm, untested 2024-10-03 16:33:47 +10:00
Kayne Ruse a0d616f412 Added CODE_OF_CONDUCT
I haven't had any major issues, but it's a good idea to cover your bases.

I also like the Ladybird Browser's CoC, so I'm stealing it.
2024-10-03 11:07:11 +10:00
Kayne Ruse ab1c9b941f Added negate opcode to equality opcode, in the second byte 2024-10-03 10:15:58 +10:00
Kayne Ruse 5db3e407b1 Update README.md 2024-10-02 15:20:28 +10:00
Kayne Ruse 71c065a6c4 Changed size_t to unsigned int 2024-10-02 03:39:38 +10:00
Kayne Ruse 7b453bc35f Reworked generic structures, read more
The following structures are now more independant:

- Toy_Array
- Toy_Stack
- Toy_Bucket
- Toy_String

I reworked a lot of the memory allocation, so now there are more direct
calls to malloc() or realloc(), rather than relying on the macros from
toy_memory.h.

I've also split toy_memory into proper array and bucket files, because
it makes more sense this way, rather than having them both jammed into
one file. This means the eventual hashtable structure can also stand on
its own.

Toy_Array is a new wrapper around raw array pointers, and all of the
structures have their metadata embedded into their allocated memory now,
using variable length array members.

A lot of 'capacity' and 'count' variables were changed to 'size_t'
types, but this doesn't seem to be a problem anywhere.

If the workflow fails, then I'll leave it for tonight - I'm too tired,
and I don't want to overdo myself.
2024-10-01 20:38:06 +10:00
Kayne Ruse 53b0fc158c Tweaked README.md, and some comments
I have an idea for the data structures...
2024-10-01 15:56:40 +10:00
Kayne Ruse a9759384cd Fixed bad tests with 64 bit platforms 2024-09-30 15:43:36 +10:00
Kayne Ruse 7b8ff8f873 Tweaked error message 2024-09-30 15:32:49 +10:00
Kayne Ruse 8d6bdb88b4 Implemented and tested Toy_String, read more
Strings are needed for the handling of identifiers in the key/value
variable storage, so I've got them working first. I used the rope
pattern, which seems to be quite an interesting approach.

I'll add comparison checks later.

Adjusted how buckets are handled in all tests, could've been an issue
down the line.

Added the build instructions to README.md.
2024-09-30 15:22:00 +10:00
Kayne Ruse c1f2e19e55 Added benchmark folder
It's just a reminder for later
2024-09-30 04:56:20 +10:00
Kayne Ruse 72cad02501 Squashed: Set up a pipeline for repl script testing
It's not ready yet, but considering how much crap this took to get
working... I'm done for the night.

commit 0f3ee91a0628654a61d47a0c41cd3e39c801b8f9
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 20:27:44 2024 +1000

    I have a titanic patience... but even the titanic sank

commit d606e0948b78bc0614c81bbea48abf5ffd9f2194
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 20:03:57 2024 +1000

    Terminated

commit 7b3b59321d349a8e361857ee3dbe955ec27cb38c
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 20:00:11 2024 +1000

    What the hell?

commit 36104b7b5a5d5487c84ab98fec1ae69487fd1a90
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 19:55:28 2024 +1000

    PLEASE

commit 0de373d10ad56cba228e9473509a527bf7a3208d
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 19:31:28 2024 +1000

    Remember to commit the file!

commit 1cb5780e2dc281ecc723b4042cd9ee1f9a3115ab
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 19:24:32 2024 +1000

    Please be correct

commit 2235d716c1d872db3744905fbd2305e65f96361a
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:58:00 2024 +1000

    Nearly there

commit eabe49130cfdbe93cc3a2056819e267f97304cf1
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:51:04 2024 +1000

    Oh green world, don't desert me now

commit f0127e2d4947ce47284f2ec72f41a5b6a62121b8
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:42:50 2024 +1000

    I'm on a highway to hell...

commit f96c0f51a48b080d11d15947940d678ae4f88024
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:41:01 2024 +1000

    OK, so there's a lot of security there.

commit af3645893a744b7cfa2638bf607373565b8e6ef9
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:33:51 2024 +1000

    I'm going to steal the declaration of independence

commit e71e1c1b4243e89a580d8b04b4d236ea66ded78c
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:27:11 2024 +1000

    Show me the money!

commit f29ba6eb96c31e9bbcf23b6ece137381f5be4050
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:12:23 2024 +1000

    Say hello to my little friend!

commit 3a5ee93884b1169abae48923161d7b47b8a45cdd
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:06:59 2024 +1000

    You had me at hello

commit 2901d92f24082173e7119a37baa12a6e968796c8
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 18:02:49 2024 +1000

    You and your little dog too!

commit 967194e0593233e17ea7737647dc8042ee3bf0aa
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:52:13 2024 +1000

    I'll get you my pretty!

commit ad48a7aec8ba7ab9e11d23104cd171169cc5ff45
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:50:14 2024 +1000

    Do you feel lucky, punk?

commit 13fa5c1e96fc7c64f71009f705b8bd69b8dfabf2
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:48:04 2024 +1000

    Frankly my dear, I don't give a damn

commit 2f15a5a7a71c22bd3f34f619887137c7cd09001b
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:20:06 2024 +1000

    One of us, one of us, one of us!

commit e696d15ea62cb5d965638e04e6f0458b19cf9e28
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:16:04 2024 +1000

    Why did it have to be snakes?

commit 90631eda6263c30b0643007fc2c7a84e5e08826d
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:07:11 2024 +1000

    No, I am your father!

commit 22e1d61caac3d37ce7fe9fc41bc3b60949fe5a81
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 17:00:18 2024 +1000

    I'm sorry Dave, I can't let you do that

commit 9aa17b8b04eb65c6c9bf8b015458f749db97494d
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:57:24 2024 +1000

    LIVE, DARN YOU, LIVE!

commit 1b7f0704d9a2744f10377384ff5f36ea7f61c2da
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:52:58 2024 +1000

    That's a whole lotta nothing

commit fd4001a9eb557afb3d6cbe409c72f7416b08db60
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:44:34 2024 +1000

    Here goes nothing

commit 7ba09ef0acda2fede952a912a8a1849e9b6c59b7
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:40:28 2024 +1000

    Oh, this is gonna be fun, trying to build on a machine I don't even have

commit 1155054552ef46ceb4c21117e35f8e5e46b26dbe
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:34:06 2024 +1000

    There is no repl-gdb in Ba Sing Se

commit 5361a31da1d9278972dcbe3c8a0ad59ea6e2ab41
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:31:05 2024 +1000

    Changed -R to -rpath

commit de982ce2846dd6ffe316648e32a8a04b9d7f242d
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:27:14 2024 +1000

    Fixed printf() string literal

commit 5ddec42af5b46a0c25f64b088a4992433e5a4116
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:24:05 2024 +1000

    Workflow experiment

commit 28570940b8758fc7f05c957055580e4286a887af
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 28 16:10:19 2024 +1000

    Fixed workflow for macos
2024-09-28 20:38:44 +10:00
Kayne Ruse 57fb1ece81 Basic infile reading is working, untested 2024-09-28 12:26:42 +10:00
Kayne Ruse 3d1d3b3b77 Working on repl, not fully working yet, read more
After a few hours struggling with the linker, I've got the main.c file
running correctly, with caveats:

- must be executed from out/
- only building on linux for the moment
- no tests written yet

I will write some CI jobs to see if the repl works eventually.
2024-09-27 22:53:10 +10:00
Kayne Ruse c518960171 Toy_VM and Toy_Stack are working and tested, read more
At this point, only a minimal number of operations are working, and
after running any kind of source code, the 'result' is simply left on
the VM's stack. Still, it's awesome to see it reach this point.
2024-09-27 15:12:37 +10:00
Kayne Ruse 0504f4af8b Began writing Toy_VM, read more
Toy_VM and Toy_Stack are both considered WIP, and neither has any tests
yet.
2024-09-26 17:17:18 +10:00
Kayne Ruse 7d92101c1f Tweaked README.md 2024-09-22 15:20:08 +10:00
Kayne Ruse c180984120 Tweaked build trigger 2024-09-22 15:03:34 +10:00
Kayne Ruse a2625fa6d4 Implemented tests for Toy_Bytecode and Toy_Routine 2024-09-22 14:13:50 +10:00
Kayne Ruse 4567484038 Squashed: platform support, bugfixes, GH workflows, read more
I've expanded support to three major platforms:

- linux
- windows
- macos

The CI now runs the test suites for all of these, both under normal
conditions and under GDB (except for macos, which lacks GDB support).

TOY_BITNESS specifies the bit-width of the current platform, either 32
or 64. A value of -1 means the bit-width could not be determined. Some
tests will be disabled if the appropriate bit-width can't be determined,
and a warning is printed to stderr.

TOY_API has been tweaked, and is now dependant on different
preprocessor flags. It is defined as 'extern' on all supported
platforms except windows, which instead specifies DLL support. It
defaults to 'extern' if the platform can't be determined.

commit d0350998ecc80b8925a1962ceb2ab400da50be9d
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sun Sep 22 09:55:42 2024 +1000

    Expanded GDB tests using matrix strategy

commit dc2addacc52830227ddcd0f35997c0e1668b579c
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sun Sep 22 09:05:42 2024 +1000

    Reserved the yield keyword

commit f485c380f74a49092e0c5a41e599fbb06dbce235
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 21 15:17:11 2024 +1000

    Potential segfault fix

commit d8b19d21c92133feb071e631009a3cf99df0f068
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 21 14:25:47 2024 +1000

    Added testing on windows under GDB, read more

    I'm hunting a segfault that only appears on windows, but I lack a
    windows machine, so github's runners are all I have right now.

commit 8606db541fb5cbe91b16a39e9815fe4a27ba0c8a
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 21 13:12:02 2024 +1000

    DLL import/export macros tweaked for windows

    TOY_EXPORT for making a DLL
    TOY_IMPORT for using a DLL

    Defaults to 'extern' if neither option is present

commit a6929666401953a5b3a93dfe83c9398e012beefc
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 21 12:52:06 2024 +1000

    Investigating bitness issue on windows

commit 8f615f735868a316e8d5a6a77ed899e72fd537f8
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 21 12:32:55 2024 +1000

    Adjusting bitness tests in test_ast.c

commit 61694f2183ac84ee7c53c855f2f6aa29f360f16c
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Sat Sep 21 11:46:59 2024 +1000

    Added experimental macOS CI job
2024-09-22 11:27:08 +10:00
Kayne Ruse ac89f80b5b Merge remote-tracking branch 'refs/remotes/origin/dev' into dev 2024-09-20 21:46:11 +10:00
Kayne Ruse 78320e53bb Multiple bugfixes, read more
* expand() will only touch the memory once
* fixed missing if-else cascade at toy_routine.c:90
* unary negate will not optimise numbers unless the '-' character is immediately prior
* fixed broken test for unary negation of groups
2024-09-20 21:39:02 +10:00
Kayne Ruse 84f26d83c8 Update README.md 2024-09-20 16:41:25 +10:00
Kayne Ruse ad6f1c3067 Bytecode and Routine working, Routine tests incomplete
There may be a build issue on windows
2024-09-20 16:22:52 +10:00
Kayne Ruse e35af3d84b Fixed buffer overflow, bytecode is WIP 2024-09-19 19:06:49 +10:00
Kayne Ruse 083ee950dd WIP bytecode and routine, read more
The tests are failing in a strange way, with the error message 'corrupted top size'. I don't know what it means, and it seems to be caused by a call to printf() within 'test_bytecode.c'. I need a break, as this is making me dizzy.
2024-09-19 12:45:35 +10:00
Kayne Ruse 47ac1c5b30 Removed Toy_AstGroup generation, as it isn't needed 2024-09-14 12:09:02 +10:00
Kayne Ruse 898b8efc04 Implemented tests for Toy_Parser
The parser now correctly produces a workable AST. I think I'll skip
over the optimizer, and begin on the compiler next session. The
optimizer will act directly on the AST, but it isn't totally necessary.

Other tools can also operate on the AST, such as for debugging - I'll
have to ask what kinds are out there.
2024-09-13 18:08:52 +10:00
Kayne Ruse b00a6838be Wrote Toy_Parser with minimal features, tests missing
It's too late at night, so I'm packing this up with only a dummy warning
message for the tests. I'll keep going tomorrow, hopefully.
2024-09-12 20:53:34 +10:00
Kayne Ruse eca3350c64 Tweaked platform support, disabled some tests based on platform 2024-09-10 21:08:05 +10:00
Kayne Ruse 5fd933a15e Implemented AST, ensured bucket memory worked 2024-09-10 20:25:01 +10:00
Kayne Ruse 81417e7f32 Implemented bucket memory structure for custom allocators 2024-09-07 19:47:02 +10:00
Kayne Ruse 023cf9c8b5 Wrote bytecode-format.txt
It's annoying that I can only work for two hours at a time
2024-08-31 21:27:50 +10:00
Kayne Ruse 65087b18bd Wrote SECD-concept.txt, probably overdid it 2024-08-30 20:25:36 +10:00
Kayne Ruse e65555ca8a Added notes about opcodes 2024-08-29 20:08:59 +10:00
Kayne Ruse 0ed676d79b Added windows-latest build to CI 2024-08-13 23:55:58 +10:00
Kayne Ruse e6ad46f1ff Wrote value, chunk, memory sections, tested value
chunk and memory remain untested for now
2024-08-13 23:42:14 +10:00
Kayne Ruse 190294e5d9 Rename continuous integration-v2.yml to continuous-integration-v2.yml 2024-08-11 21:28:08 +10:00
Kayne Ruse 2370d5dc83 Fixed CI 2024-08-11 21:12:35 +10:00
Kayne Ruse a912b6a29c Added lexer, implemented tests build system 2024-08-11 20:15:09 +10:00
Kayne Ruse 82f63013d8 Squashed commit of the following:
commit 633500eeaf72e7e5aed90f3eb071f56d93e129eb
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Aug 9 23:16:55 2024 +1000

    Please work...

commit 4b524a27fd1fe11e02206853b84bdfb349be5bf9
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Aug 9 22:57:07 2024 +1000

    This is starting to get annoying.

commit 5e16a87f6cef7ca9eb536bcc172fd4a184c20124
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Aug 9 22:38:31 2024 +1000

    Fixed workflow file

    Used this: https://rhysd.github.io/actionlint/

commit 7fd6dd610ee3e9327350859b047b0c4792e74f19
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Aug 9 22:30:50 2024 +1000

    Let's try again

commit d83b42a894929f926ba5bf94dee8a2a495a4db86
Author: Kayne Ruse <kayneruse@gmail.com>
Date:   Fri Aug 9 22:27:36 2024 +1000

    Reworked the CI

    Also checked over the new code.
2024-08-09 23:22:54 +10:00
Ratstail91 0b8bf4119f Added lists of tokens and keywords, ready for use 2024-05-20 02:35:19 +10:00
Ratstail91 607dc2c22a Added tests-full workflow as a placeholder
This is mainly so github stops emailing me when I push to dev
2024-05-19 12:53:15 +10:00
Ratstail91 fbcb2e0331 Began picking through the old logic 2024-05-19 04:26:15 +10:00
Ratstail91 361fa78ffb Initial outline of non-code files 2024-05-19 03:50:57 +10:00
220 changed files with 17449 additions and 17814 deletions
+10
View File
@@ -0,0 +1,10 @@
---
name: Question
about: Ask a Question
labels: question
---
### How can I help?
I'm always here to help with any inquiries you have regarding Toy and its related projects.
-34
View File
@@ -1,34 +0,0 @@
name: Comprehensive Tests
on:
push:
branches: [ "main", "dev" ]
pull_request:
branches: [ "main" ]
jobs:
test-valgrind:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: install valgrind
run: sudo apt install valgrind
- name: make test (valgrind)
run: make test
test-sanitized:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@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
@@ -0,0 +1,42 @@
name: Continuous Integration v2.x
#trigger when these occur
on:
push:
branches:
- v2
pull_request:
types:
- opened
- edited
- reopened
branches:
- v2
workflow_dispatch:
jobs:
#CI workflows across all supported platforms
standard:
strategy:
fail-fast: false
matrix:
platforms:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.platforms }}
steps:
- uses: actions/checkout@v6
- name: Run all tests
run: make tests
gdb:
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install GDB if not present
run: sudo apt update && sudo apt install gdb
- name: Run all tests under gdb
run: make tests-gdb
+50 -28
View File
@@ -1,31 +1,53 @@
#Editor generated files
*.sln
*.vcproj
*.suo
*.ncb
*.user
compile_commands.json
# Prerequisites
*.d
#Directories
Release/
Debug/
Out/
release/
debug/
out/
.cache/
#Project generated files
*.db
# Object files
*.o
*.a
*.exe
*.meta
*.log
out
*.stackdump
*.tb
*.ko
*.obj
*.elf
#Shell files
*.bat
*.sh
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
*.log
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
+2
View File
@@ -0,0 +1,2 @@
This folder is full of development notes, and are probably out of date. Check the actual docs for the correct info.
+152
View File
@@ -0,0 +1,152 @@
This file is messy and confusing, and makes sense to nobody but me - so don't worry about understanding it too much - better docs will come later.
===
SECD = State, Environment, Control, Dump
The idea of "Landin's SECD Machine" is to store the working memory in S, the variable-value bindings in E, the code/instructions in C, and the program stack in D.
Notes:
DEFINE = DECLARE + SET
The environment, denoted with an E, is created on routine start, and destroyed on routine end - however, it uses the parent routine's environment as the starting point for it's creation, so closures work as expected
unlike version 1, identifiers are not a valid datatype - they're just an index representing a symbol, like "standard::clock"
meta opcodes - EOF, PASS, ERROR,
a "value" can be of any valid datatype, and may point to various parts of memory to define it's value
Symbols will be awkward... I suspect the symbol table might need to be rebuilt on startup, as the order of the modules will not necessarily be the same each time
The various instances of S could be the same array in memory, simply marked as "unused"? You could stick C on there as a value before "pushing" for a new routine
Things to consider later:
type cast?
rest parameter?
index access and assign?
===
//variable instructions
READ
read one value from C onto S
LOAD
read one value from .data onto S
DECLARE
read two words from C, create a new entry in E with the key E[SYMBOL(word1)], the type defined by word2, the value 'null'
DEFINE
read one word from C, saves the pre-existing key E[SYMBOL(word)] to the value S(0), popping S(0)
ACCESS
read one word from C, finds the pre-existing value of E[SYMBOL(word)], leaves the value on S
//arithmetic instructions
ADD
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
SUBTRACT
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
MULTIPLY
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
DIVIDE
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
MODULO
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
//comparison instructions
COMPARE_EQUAL
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on equality
COMPARE_LESS
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
COMPARE_LESS_EQUAL
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
COMPARE_GREATER
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
COMPARE_GREATER_EQUAL
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
//logical instructions
AND
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
OR
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
TRUTHY
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
NEGATE
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
//control instructions
JUMP
read one value from C, and move the program counter to that location (relative to the current position)
JUMP_IF_FALSE
read one value from C, pops S(0), and move the program counter to that location (relative to the current position) if the popped value is falsy
FN_CALL
*read a list of arguments specified in C into 'A', store (S, E, C, D) as D, push S, move the stack pointer to the specified routine, push a new E based on the contents of 'A'
FN_RETURN
*read a list of return values specified in C into 'R', pop S, restore (S, E, C, D) from D(0) popping it, store the contents of 'R' in E or S based on the next few parts of C
//various action instructions
ASSERT
if S(-1) is falsy, print S(0) and exit
PRINT
pop S(0), and print the output
IMPORT
//invoke an external library into the current scope
CONCAT
//combine two strings
SCOPE_BEGIN
//push an inner environment to E, which should be automatically popped at the routine's end
SCOPE_END
//pop an inner environment from E, only if it was created with SCOPE_BEGIN
===
FN_CALL
read word: read the following N arguments
for 0 to N do:
read word as match: # this allows literals and identifiers as arguments
stack: then pop S(0) into 'A'
**env: then read word, load E[SYMBOL(word)] into 'A'
read word:
determine where the routine is (is it new or is it a value?) and hold it for a moment
push E and C into a frame marker on S
jump C to the routine
read word:
read the following N parameter names, storing each member of 'A' as their value in E[SYMBOL(name)]
continue
FN_RETURN
read word: read the following N return values
for 0 to N do:
read word as match: # this allows literals and identifiers as arguments
stack: then pop S(0) into 'R'
**env: then read word, load E[SYMBOL(word)] into 'R'
pop E and S
extract and restore E and C from the frame marker on S
read word: read the following N storage locations for the values within `R`
for 0 to N do:
read word as match: # you're effectively reversing the prior reads
stack: then push from 'R' onto S
**env: then read word, save 'R' into E[SYMBOL(word)]
**This could work by listing the sources as e.g. "SSSExS" - three stacks and one environment variable loaded onto the stack, then one more stack for a total of four values
Notes:
the bytecode of a funtion call would look like:
FN_CALL N [stack|env word]... N [stack|env word]...
the value of C stored in D points to the second N, while it waits to pick up where it left off
===
+64
View File
@@ -0,0 +1,64 @@
The bytecode format
===
NOTE: This datestamp header is currently not implemented
There are four components in the datestamp header:
TOY_VERSION_MAJOR
TOY_VERSION_MINOR
TOY_VERSION_PATCH
TOY_VERSION_BUILD
The first three are each one unsigned byte, and the fourth is a null terminated C-string.
* Under no circumstance, should you ever run bytecode whose major version is different
* Under no circumstance, should you ever run bytecode whose minor version is above the interpreters minor version
* You may, at your own risk, attempt to run bytecode whose patch version is different from the interpreters patch version
* You may, at your own risk, attempt to run bytecode whose build version is different from the interpreters build version
An additional note: The contents of the build string may be anything, such as:
* the compilation date and time of the interpreter
* a marker identifying the current fork and/or branch
* identification information, such as the developer's copyright
* a link to Risk Astley's "Never Gonna Give You Up" on YouTube
Please note that in the final bytecode, if the null terminator of TOY_VERSION_BUILD is not 4-byte aligned, extra space will be allocated to round out the header's size to a multiple of 4. The contents of the extra bytes are undefined.
===
Bytecode Format Structure
.header:
N total size # size of this routine, including all data and subroutines
N .jumps count # the number of entries in the jump table (should be data count + routine count)
N .param count # the number of parameter fields expected (a secondary jump table, used for subroutine parameters)
N .data count # the number of data fields present
N .subs count # the number of subroutines present
.code start # absolute address of .code; mandatory
.param start # absolute addess of .param; omitted if not needed
.datatable start # absolute address of .datatable; omitted if not needed
.data start # absolute address of .data; omitted if not needed
.subs start # absolute address of .subs; omitted if not needed
# additional metadata fields can be added later
.code:
# opcode instructions read and 'executed' by the interpreter (aligned to 4-byte widths)
[READ, TOY_VALUE_STRING, Toy_StringType, stringLength] [jumpIndex]
.jumps:
# a layer of indirection for quickly looking up values in .data and .subs
0 -> {string, 0x00}
4 -> {fn, 0xFF}
.param:
# a list of names, stored in .data, to be used for any provided function arguments
.data:
# data that can't be cleanly embedded into .code, such as strings
"Hello world\0"
.subs:
# an extension of .data, used exclusively for subroutines (they also follow this spec, recursively)
+23
View File
@@ -0,0 +1,23 @@
#include <stdio.h>
#include <stdint.h>
uint32_t hash (uint32_t x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x);
return x;
}
uint32_t unhash ( uint32_t x ) {
x = (( x >> 16) ^ x) * 0x119de1f3;
x = (( x >> 16) ^ x) * 0x119de1f3;
x = (( x >> 16) ^ x);
return x;
}
int main() {
//I legit didn't know this algorithm could be reversed. Neat.
uint32_t value = 42;
printf("%u %u %u", value, hash(value), unhash(hash(value)));
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
The default version of GCC that ships on Raspian has an issue. The file '/usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so' has a faulty implementation of 'memcpy()' and possibly 'memset()'. Changing to the newer versions doens't work, as they're just symlinks to v7.
To resolve this, download and build this shared object:
https://github.com/simonjhall/copies-and-fills
Then, set the linker's preload value to point to that '.so' file (You may need to edit '/etc/ld.so.preload')
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"gruntfuggly.todo-tree"
]
}
+66
View File
@@ -0,0 +1,66 @@
{
"todo-tree.filtering.includeGlobs": [
"**/repl/**",
"**/scripts/**",
"**/source/**",
"**/tests/**",
"**/tools/**",
],
"todo-tree.filtering.excludeGlobs": [
"**/obj/**",
"**/out/**",
],
"todo-tree.general.tags": [
"URGENT",
"BUG",
"TODO",
"WARN",
"BUGFIX",
"WONTFIX",
"NOTE"
],
"todo-tree.highlights.customHighlight": {
"URGENT": {
"icon": "alert",
"type": "text",
"iconColour": "#FF0000",
"foreground": "#FF0000"
},
"BUG": {
"icon": "bug",
"type": "text",
"iconColour": "#FF0000",
"foreground": "#FF0000"
},
"TODO": {
"icon": "alert",
"type": "text",
"iconColour": "#FFFF00",
"foreground": "#FFFF00"
},
"WARN": {
"icon": "alert",
"type": "text",
"iconColour": "#FFA500",
"foreground": "#FFA500"
},
"BUGFIX": {
"icon": "bug",
"type": "text",
"iconColour": "#00A000",
"foreground": "#00A000"
},
"WONTFIX": {
"icon": "bug",
"type": "text",
"iconColour": "#B64949",
"foreground": "#B64949"
},
"NOTE": {
"icon": "alert",
"type": "text",
"iconColour": "#00A000",
"foreground": "#00A000"
},
}
}
+1
View File
@@ -0,0 +1 @@
No hating on other people, OK?
+17
View File
@@ -0,0 +1,17 @@
Copyright (c) 2020-2026 Kayne Ruse, KR Game Studios
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
-13
View File
@@ -1,13 +0,0 @@
# License
Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+146
View File
@@ -0,0 +1,146 @@
# Toy v2 Quick-Start Guide
To help you start using Toy as fast as possible, here are the most useful elements of the language. Not everything available is listed, but this should let you start coding right away.
## Keyword 'print'
The `print` keyword takes one value as a parameter, which is sent to stdout by default, or can be redirected elsewhere using C.
```
print "Hello World!";
```
## Keyword 'assert'
The `assert` keyword takes two values as parameters, separated by a comma. If the first value is falsy or `null`, the optional second parameter is sent to stderr by default, or can be redirected elsewhere using C. If no second parameter is provided, a generic error message is used instead.
```
//nothing happens
assert 1 < 2;
//this assert will fail, and output the second parameter
assert null, "Hello world!";
```
## Variables and Types
Variables can be declared with the `var` keyword, and can be given an optional type from the list below. If no type is specified, `any` is used by default.
```
var answer = 42;
var question: string = "How many roads must a man walk down?";
```
To make a variable immutable, use the `const` keyword after the type declaration. In this case, it must be assigend a value.
```
var quote: string const = "War. War never changes.";
```
The types available in Toy are:
| type | name | description |
| --- | --- | --- |
| `bool` | boolean | Either `true` or `false`. |
| `int` | integer | Any whole number (32-bits). |
| `float` | float | A decimal number (32-bits), using floating-point arithmetic. |
| `string` | string | A series of characters used for text processing. |
| `array` | array | A series of values stored sequentially in memory. |
| `table` | table | A series key-value pairs stored in such a way that allows for fast lookups. Booleans, functions, opaques and `null` can't be used as keys. |
| `function` | function | A chunk of reusable code that takes zero or more parameters, and returns zero or more results. Functions are declared with the `fn` keyword. |
| `opaque` | opaque | This value is unusable in Toy, but allows you to pass data between C functions. |
| `any` | any | The default type when nothing is specified. Theis can hold any value. |
## Control Flow
Choosing an option, or repeating a chunk of code multiple times, is essential for any general purpose language.
Choosing between two options can be done with the `if-then-else` else statement. If the condition is truthy, the 'then-branch' will be executed. Otherwise, the optional 'else-branch' is executed instead.
```
var answer = 42;
if (answer < 56) {
print "Cod dang it!";
}
else {
print "Something's fishy here...";
}
```
```
var challenge = "hard";
if (challenge == "hard") {
print "I choose to build a scripting language, not because it's easy, but because it's hard!";
}
//the else-branch is optional
```
To repeat a certain action, use the `while-then` loop, which repeats the body as long as the condition is true at the beginning of each loop.
```
var loops = 0;
while (loops++ < 8) {
print "These episodes are endless.";
}
```
To break out of a loop, you can use the `break` keyword. Alternatively, to restart the loop early, use the `continue` keyword.
```
var loops = 0;
while (true) {
if (++loops < 15532) {
continue;
}
break; //poor yuki ;_;
}
```
*Note: The `for` loop is coming, eventually, but isn't vital right now.*
## Arrays and Tables
Arrays are defined with a pair of brackets, and can contain a list of comma-separated values.
```
//'array' is a reserved keyword, so it can't be used as a name
var a = [1,2,3];
//instead, it's used as a type
var b: array = [4,5,6];
//define an empty array like this
var c: array = [];
//arrays are zero-indexed
print a[0]; //'1'
```
Tables are also defined with brackets, and contain a comma-separated list of key-value pairs defined by colons:
```
//most types can be used as keys
var t = ["alpha": 1, "beta": 2, "gamma": 3];
//the 'table' keyword can define the type, and an empty table still has a colon
var u: table = [:];
//printing a table does NOT guarantee internal order, but the elements can be accessed with the keys.
print t["beta"];
```
## Functions
Watch this space.
## External Libraries and Extending Toy
Watch this space.
+38 -46
View File
@@ -1,71 +1,63 @@
<p align="center">
<image src="toylogo.png" />
<image src="toylogo.png" />
</p>
# Toy
**This is a work in progress, and is not yet fit for purpose. I hope I can get it to a useable state, but personal issues can often make dedicating myself to a project difficult. Your patience and support is greatly appreciated.**
This is the Toy programming language interpreter, written in C.
# Toy v2.x
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through text files.
This repository holds the reference implementation for Toy version 2.x, written in C.
# Nifty Features
* Simple C-like syntax
* Bytecode intermediate compilation
* Optional, but robust type system (including `opaque` for arbitrary data)
* Functions and types are first-class citizens
* 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
* Simple C-style syntax
* Intermediate AST and bytecode representations
* Strong, but optional type system
* First-class functions and closures
* Extensible with imported native code
* Can re-direct output, error and assert failure messages
* Open-Source under the zlib license
## Building
# Syntax
For Windows(mingw32 & cygwin), Linux and MacOS, simply run `make` in the root directory.
Watch this space.
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
(The `scripts` or `tests` directory might help, the docs website is WIP.)
## Tools
# Building
Run `make install-tools` to install a number of tools, including:
This project requires `gcc` and `make` by default, but should also work in other environments. Officially supported platforms include `linux`, `windows` and `macOS`, see `source/toy_common.h` for implementation details.
* VSCode syntax highlighting
Run `make` in the root directory to build the shared library named `libToy.so` and a useable REPL named `repl.out`.
## Syntax
# Tools
```
import standard; //for a bunch of utility functions
Watch this space.
print "Hello world"; //"print" is a keyword
(There's some utility functions in `repl/` that are WIP but useful.)
var msg = "foobar"; //declare a variable like this
# Documentation
assert true, "This message won't be seen"; //assert is another keyword
//-------------------------
fn makeCounter() { //declare a function like this
var total: int = 0; //declare a variable with a type like this
fn counter(): int { //declare a return type like this
return ++total;
}
return counter; //closures are explicitly supported
}
var tally = makeCounter();
print tally(); //1
print tally(); //2
print tally(); //3
```
Watch this space.
# License
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
This source code is covered by the Zlib license (see [LICENSE](LICENSE) for details).
# Patrons via Patreon
# Contributors and Special Thanks
* Seth A. Robinson
@NishiOwO - Unofficial NetBSD support
@Gipson62 - v1 docs spell checking
@8051Enthusiast - `fixAlignment()` trick
@hiperiondev - v1 Disassembler, v1 porting support and feedback
@add00 - v1 Library support
@gruelingpine185 - Unofficial v1 MacOS support
@solar-mist - v1 Minor bugfixes
Various Anons - Feedback
@munificent - For [writing the book](http://craftinginterpreters.com/) that sparked my interest in langdev
# Patreon Supporters
You can show your support and be listed here by joining my [Patreon](https://patreon.com/krgamestudios).
+63 -70
View File
@@ -1,90 +1,83 @@
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
# export CFLAGS+=-fsanitize=address,undefined
#compiler settings reference
#CC=gcc
#CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
#LIBS+=-lm
#LDFLAGS+=
export CFLAGS+=-std=c18 -pedantic -Werror
#TODO: release builds should define the NDEBUG flag; double check it works
export TOY_OUTDIR = out
#directories
export TOY_SOURCEDIR=source
export TOY_REPLDIR=repl
export TOY_OUTDIR=out
export TOY_OBJDIR=obj
all: $(TOY_OUTDIR) repl
#targets
all: source repl
#repl builds
repl: $(TOY_OUTDIR) library
$(MAKE) -C repl
.PHONY: source
source:
$(MAKE) -C source -k
repl-static: $(TOY_OUTDIR) static
$(MAKE) -C repl
.PHONY: repl
repl: source
$(MAKE) -C repl -k
repl-release: clean $(TOY_OUTDIR) library-release
$(MAKE) -C repl release
.PHONY: tests tests-ci
tests: clean
$(MAKE) -C tests -k
repl-static-release: clean $(TOY_OUTDIR) static-release
$(MAKE) -C repl release
#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
tests-gdb: clean
$(MAKE) -C tests -k gdb
#util targets
$(TOY_OUTDIR):
mkdir $(TOY_OUTDIR)
#utils
install-tools:
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
$(TOY_OBJDIR):
mkdir $(TOY_OBJDIR)
#util commands
.PHONY: clean
clean:
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
find . -empty -type d -delete
else ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
rm -rf out
find . -empty -type d -delete
ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.out' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(shell uname),NetBSD)
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.out' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib
$(RM) out
$(RM) obj
else ifeq ($(shell uname),Darwin)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.dylib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
rm -rf out
find . -empty -type d -delete
find . -type f -name '*.o' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.out' -delete
find . -type f -name '*.exe' -delete
find . -type f -name '*.dll' -delete
find . -type f -name '*.lib' -delete
find . -type f -name '*.so' -delete
find . -type f -name '*.dylib' -delete
find . -type d -name 'out' -delete
find . -type d -name 'obj' -delete
else
@echo "Deletion failed - what platform is this?"
endif
rebuild: clean all
+100
View File
@@ -0,0 +1,100 @@
#include "ast_inspector.h"
#include "toy_console_colors.h"
#include "toy_bucket.h"
#include "toy_string.h"
#include "toy_value.h"
#include <stdio.h>
#include <stdlib.h>
void inspect_by_type(Toy_Ast* ast, int depth);
void inspect_block(Toy_Ast* ast, int depth);
void inspect_value(Toy_Ast* ast, int depth);
void inspect_print(Toy_Ast* ast, int depth);
#define PRINTSTR(x) printf("%*s%s", depth*4, "", x);
static Toy_Bucket* bucket = NULL; //lazy
void inspect_ast(Toy_Ast* ast) {
bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
inspect_by_type(ast, 0);
Toy_freeBucket(&bucket);
}
void inspect_by_type(Toy_Ast* ast, int depth) {
switch(ast->type) {
case TOY_AST_BLOCK:
inspect_block(ast, depth);
return;
case TOY_AST_VALUE:
inspect_value(ast, depth);
return;
// case TOY_AST_UNARY:
// case TOY_AST_BINARY:
// case TOY_AST_BINARY_SHORT_CIRCUIT:
// case TOY_AST_COMPARE:
// case TOY_AST_GROUP:
// case TOY_AST_COMPOUND:
// case TOY_AST_AGGREGATE:
// case TOY_AST_ASSERT:
// case TOY_AST_IF_THEN_ELSE:
// case TOY_AST_WHILE_THEN:
// case TOY_AST_BREAK:
// case TOY_AST_CONTINUE:
// case TOY_AST_RETURN:
case TOY_AST_PRINT:
inspect_print(ast, depth);
return;
// case TOY_AST_VAR_DECLARE:
// case TOY_AST_VAR_ASSIGN:
// case TOY_AST_VAR_ACCESS:
// case TOY_AST_FN_DECLARE:
// case TOY_AST_FN_INVOKE:
// case TOY_AST_STACK_POP:
default:
printf(TOY_CC_WARN "%*sAST %s (unhandled by inspector)" TOY_CC_RESET "\n", depth*4, "", Toy_private_getAstTypeAsCString(ast->type));
}
}
void inspect_block(Toy_Ast* ast, int depth) {
//show the block braces
PRINTSTR("{\n");
if (ast->block.child) {
inspect_by_type(ast->block.child, depth + 1);
if (ast->block.next) {
inspect_block(ast->block.next, depth + 0);
}
}
PRINTSTR("}\n");
}
void inspect_value(Toy_Ast* ast, int depth) {
(void)depth;
Toy_String* str = Toy_stringifyValue(&bucket, ast->value.value);
char* buffer = Toy_getStringRaw(str); //SLOW
printf("%s '%s'", Toy_getValueTypeAsCString(ast->value.value.type), buffer);
free(buffer);
Toy_freeString(str);
}
void inspect_print(Toy_Ast* ast, int depth) {
(void)depth;
PRINTSTR("PRINT ");
inspect_by_type(ast->print.child, depth);
printf(";\n");
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_ast.h"
void inspect_ast(Toy_Ast* astHandle);
+47
View File
@@ -0,0 +1,47 @@
#include "bucket_inspector.h"
#include <toy_string.h>
#include <stdio.h>
int inspect_bucket(Toy_Bucket** bucketHandle) {
int depth = 0;
//for each bucket
for (Toy_Bucket* iter = (*bucketHandle); iter != NULL; iter = iter->next) {
int occupied = 0;
int released = 0;
unsigned char* ptr = iter->data;
while ((ptr - iter->data < iter->count) && *((int*)ptr) != 0) { //for each partition
if ( ( *((int*)ptr) & 1) == 0) { //is this partition still in use?
occupied++;
//try to print as a string if possible
Toy_String* str = (void*)(ptr + 4);
if (str->info.type == TOY_STRING_LEAF && str->info.length < 255) {
printf("String Leaf (%d bytes, %d refCount): %.*s\n", *((int*)ptr), str->info.refCount, str->info.length, str->leaf.data);
}
else if (str->info.type == TOY_STRING_NODE) {
printf("String Node (%d bytes, %d refCount): ...\n", *((int*)ptr), str->info.refCount);
}
}
else {
released++;
}
//jump distance: ((*((int*)ptr) | 1) ^ 1) + 4
// printf(" jump %d, ", ((*((int*)ptr) | 1) ^ 1) + 4);
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
}
printf("Bucket link %d: count %u, %d occupied, %d released\n", depth, iter->count, occupied, released);
depth++;
}
printf("\n");
return depth;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_bucket.h"
int inspect_bucket(Toy_Bucket** bucketHandle);
+389
View File
@@ -0,0 +1,389 @@
#include "bytecode_inspector.h"
#include "toy_console_colors.h"
#include "toy_opcodes.h"
#include "toy_value.h"
#include "toy_string.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr);
int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr);
#define ISPRINT_SANITIZE(x) (isprint((int)x) > 0 ? (x) : '_')
#define MARKER_VALUE(pc, type) \
((unsigned int)(pc * sizeof(type)))
#define MARKER "\t\033[" TOY_CC_FONT_BLACK "m" " %u\t" TOY_CC_RESET
#define FONT_BLACK "\033[" TOY_CC_FONT_BLACK "m"
//exposed functions
int inspect_bytecode(unsigned char* bytecode) {
//TODO: handle version info
unsigned int const bytecodeSize = ((unsigned int*)(bytecode))[0];
unsigned int const jumpsSize = ((unsigned int*)(bytecode))[1];
unsigned int const paramSize = ((unsigned int*)(bytecode))[2];
unsigned int const dataSize = ((unsigned int*)(bytecode))[3];
unsigned int const subsSize = ((unsigned int*)(bytecode))[4];
printf(FONT_BLACK ".header:\r" TOY_CC_RESET);
//bytecode size
printf(MARKER TOY_CC_NOTICE "Bytecode Size: \t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(0, unsigned int), bytecodeSize);
//header counts
printf(MARKER TOY_CC_NOTICE "Jumps Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(1, unsigned int), jumpsSize);
printf(MARKER TOY_CC_NOTICE "Param Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(2, unsigned int), paramSize);
printf(MARKER TOY_CC_NOTICE "Data Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(3, unsigned int), dataSize);
printf(MARKER TOY_CC_NOTICE "Subs Size:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(4, unsigned int), subsSize);
//some addresses may be absent
unsigned int addr_pc = 4;
unsigned int code_addr = 0;
unsigned int jumps_addr = 0;
unsigned int param_addr = 0;
unsigned int data_addr = 0;
unsigned int subs_addr = 0;
//bugfix
unsigned int code_end = 0;
//header addresses
if (true) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Code Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
code_addr = ((unsigned int*)(bytecode))[addr_pc];
}
if (jumpsSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Jumps Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
jumps_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = jumps_addr;
}
if (paramSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Param Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
param_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = param_addr;
}
if (dataSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Data Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
data_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = data_addr;
}
if (subsSize > 0) {
addr_pc++;
printf(MARKER TOY_CC_NOTICE "Subs Address:\t\t%u" TOY_CC_RESET "\n", MARKER_VALUE(addr_pc, unsigned int), ((unsigned int*)(bytecode))[addr_pc]);
subs_addr = ((unsigned int*)(bytecode))[addr_pc];
if (code_end == 0) code_end = subs_addr;
}
if (code_end == 0) code_end = bytecodeSize; //very hacky
printf(FONT_BLACK ".code:\r" TOY_CC_RESET);
unsigned int pc = code_addr;
while(pc < code_end) {
pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr);
}
//jumps
if (jumpsSize > 0) {
printf(FONT_BLACK ".jumps:\r" TOY_CC_RESET);
for (unsigned int i = 0; (i*4) < jumpsSize; i++) {
printf(MARKER TOY_CC_NOTICE "%u (data %u)" TOY_CC_RESET "\n", MARKER_VALUE(jumps_addr + i, unsigned int),
i,
((unsigned int*)(bytecode + jumps_addr))[i] + data_addr
);
}
}
//param
if (paramSize > 0) {
printf(FONT_BLACK ".param:\r" TOY_CC_RESET);
for (unsigned int i = 0; (i*4) < paramSize; i += 2) {
printf(MARKER TOY_CC_NOTICE "%u (type %s, data %u)" TOY_CC_RESET "\n", MARKER_VALUE(param_addr + i, unsigned int),
i,
Toy_getValueTypeAsCString(((unsigned int*)(bytecode + param_addr))[i + 1]),
((unsigned int*)(bytecode + param_addr))[i] + data_addr
);
}
}
//data; assume there's only strings for now
if (dataSize > 0) {
printf(FONT_BLACK ".data:\r" TOY_CC_RESET);
for (unsigned int i = 0; (i*4) < dataSize; i++) {
printf(MARKER TOY_CC_NOTICE "%c %c %c %c" TOY_CC_RESET "\n", MARKER_VALUE(data_addr + i, unsigned int),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[0]),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[1]),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[2]),
ISPRINT_SANITIZE(((char*)(bytecode + data_addr + (i*4)))[3])
);
}
}
//subs
if (subsSize > 0) {
printf(FONT_BLACK ".subs:\n" TOY_CC_RESET);
unsigned int i = 0;
while (i < subsSize) {
i += inspect_bytecode(bytecode + subs_addr + i);
}
}
return bytecodeSize;
}
int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr) {
//read and print the opcode instruction at 'pc'
Toy_OpcodeType opcode = bytecode[pc];
switch(opcode) {
case TOY_OPCODE_READ:
return inspect_read(bytecode, pc, jumps_addr, data_addr);
case TOY_OPCODE_DECLARE: {
unsigned int indexValue = *((unsigned int*)(bytecode + pc + 4));
unsigned int jumpValue = *((unsigned int*)(bytecode + jumps_addr + indexValue));
char* cstr = ((char*)(bytecode + data_addr + jumpValue));
printf(MARKER "DECLARE %s: %s%s\n", MARKER_VALUE(pc, unsigned char),
cstr,
Toy_getValueTypeAsCString(bytecode[pc + 1]),
bytecode[pc + 3] ? " const" : ""
);
return 8;
}
case TOY_OPCODE_ASSIGN:
printf(MARKER "ASSIGN %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "(chained)" : "");
return 4;
case TOY_OPCODE_ASSIGN_COMPOUND:
printf(MARKER "ASSIGN COMPOUND %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "(chained)" : "");
return 4;
case TOY_OPCODE_ACCESS:
printf(MARKER "ACCESS\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_INVOKE:
printf(MARKER "INVOKE as '%s' (%d parameters)\n", MARKER_VALUE(pc, unsigned char),
Toy_getValueTypeAsCString(bytecode[pc + 1]),
bytecode[pc + 2]);
return 4;
case TOY_OPCODE_ATTRIBUTE:
printf(MARKER "ATTRIBUTE (accessed from a compound)\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_DUPLICATE:
printf(MARKER "DUPLICATE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] ? "and ACCESS" : "");
return 4;
case TOY_OPCODE_ELIMINATE:
printf(MARKER "ELIMINATE\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_ADD:
printf(MARKER "ADD %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_SUBTRACT:
printf(MARKER "SUBTRACT %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_MULTIPLY:
printf(MARKER "MULTIPLY %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_DIVIDE:
printf(MARKER "DIVIDE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_MODULO:
printf(MARKER "MODULO %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4;
case TOY_OPCODE_COMPARE_EQUAL:
printf(MARKER "COMPARE %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] != TOY_OPCODE_NEGATE ? "==" : "!=");
return 4;
case TOY_OPCODE_COMPARE_LESS:
printf(MARKER "COMPARE '<'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_COMPARE_LESS_EQUAL:
printf(MARKER "COMPARE '<='\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_COMPARE_GREATER:
printf(MARKER "COMPARE '>'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_COMPARE_GREATER_EQUAL:
printf(MARKER "COMPARE '>='\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_AND:
printf(MARKER "LOGICAL '&&'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_OR:
printf(MARKER "LOGICAL '||'\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_TRUTHY:
printf(MARKER "LOGICAL TRUTHY\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_NEGATE:
printf(MARKER "LOGICAL NEGATE\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_RETURN:
printf(MARKER "Keyword RETURN (%u)\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1]);
return 4;
case TOY_OPCODE_JUMP:
printf(MARKER TOY_CC_DEBUG "JUMP %s%s (%s%d) (GOTO %u)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 1] == TOY_OP_PARAM_JUMP_ABSOLUTE ? "absolute" : "relative",
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_ALWAYS ? "" :
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_TRUE ? " if true" : " if false",
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
bytecode[pc + 4],
bytecode[pc + 4] + pc + 8
);
return 8;
case TOY_OPCODE_ESCAPE:
printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
bytecode[pc + 4],
bytecode[pc + 4] + pc + 12,
bytecode[pc + 8]
);
return 12;
case TOY_OPCODE_SCOPE_PUSH:
printf(MARKER "SCOPE PUSH\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_SCOPE_POP:
printf(MARKER "SCOPE POP\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_ASSERT:
printf(MARKER TOY_CC_WARN "Keyword ASSERT (cond%s)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] > 1 ? ",msg" : "");
return 4;
case TOY_OPCODE_PRINT:
printf(MARKER TOY_CC_NOTICE "Keyword PRINT\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_CONCAT:
printf(MARKER "CONCATENATE strings\n", MARKER_VALUE(pc, unsigned char));
return 4;
case TOY_OPCODE_INDEX:
printf(MARKER "INDEX (%d elements)\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1]);
return 4;
// case TOY_OPCODE_UNUSED:
// case TOY_OPCODE_PASS:
// case TOY_OPCODE_ERROR:
// case TOY_OPCODE_EOF:
default:
printf(MARKER TOY_CC_WARN "Unknown Word: [%u, %u, %u, %u]" TOY_CC_RESET "\n", MARKER_VALUE(pc, unsigned char), bytecode[pc], bytecode[pc+1], bytecode[pc+2], bytecode[pc+3]);
return 4;
}
}
int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_addr, unsigned int data_addr) {
Toy_ValueType type = bytecode[pc + 1];
switch(type) {
case TOY_VALUE_NULL: {
printf(MARKER "READ NULL\n", MARKER_VALUE(pc, unsigned char));
return 4;
}
case TOY_VALUE_BOOLEAN: {
if (bytecode[pc + 2]) {
printf(MARKER "READ BOOL true\n", MARKER_VALUE(pc, unsigned char));
}
else {
}
return 4;
}
case TOY_VALUE_INTEGER: {
int i = *(int*)(bytecode + pc + 4);
printf(MARKER "READ INTEGER %d\n", MARKER_VALUE(pc, unsigned char), i);
return 8;
}
case TOY_VALUE_FLOAT: {
float i = *(float*)(bytecode + pc + 4);
printf(MARKER "READ FLOAT %f\n", MARKER_VALUE(pc, unsigned char), i);
return 8;
}
case TOY_VALUE_STRING: {
Toy_StringType stringType = (Toy_StringType)(*(bytecode + pc + 2)); //Probably not needed
int len = bytecode[pc + 3]; //only used for names?
(void)len;
(void)stringType;
unsigned int indexValue = *((unsigned int*)(bytecode + pc + 4));
unsigned int jumpValue = *((unsigned int*)(bytecode + jumps_addr + indexValue));
char* cstr = ((char*)(bytecode + data_addr + jumpValue));
printf(MARKER "READ STRING [%u] '%s'\n", MARKER_VALUE(pc, unsigned char), indexValue, cstr);
return 8;
}
case TOY_VALUE_FUNCTION:
printf(MARKER "READ FUNCTION '%u' (%d params)\n", MARKER_VALUE(pc, unsigned char), *((unsigned int*)(bytecode + pc + 4)), bytecode[pc + 2]);
return 8;
case TOY_VALUE_ARRAY: {
unsigned int count = *((unsigned int*)(bytecode + pc + 4));
printf(MARKER "READ ARRAY %u elements\n", MARKER_VALUE(pc, unsigned char), count);
return 8;
}
case TOY_VALUE_TABLE: {
unsigned int count = *((unsigned int*)(bytecode + pc + 4));
printf(MARKER "READ TABLE %u elements (consuming %u values)\n", MARKER_VALUE(pc, unsigned char), count / 2, count);
return 8;
}
case TOY_VALUE_OPAQUE:
case TOY_VALUE_ANY:
case TOY_VALUE_UNKNOWN:
default: {
printf(MARKER TOY_CC_WARN "READ %s (unhandled by inspector)" TOY_CC_RESET "\n", MARKER_VALUE(pc, unsigned char), Toy_getValueTypeAsCString(type));
return 4;
}
}
}
//TODO: Check if strings are reused in the bytecode
+3
View File
@@ -0,0 +1,3 @@
#pragma once
int inspect_bytecode(unsigned char* bytecode);
-162
View File
@@ -1,162 +0,0 @@
#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
@@ -1,6 +0,0 @@
#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
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
-646
View File
@@ -1,646 +0,0 @@
#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
@@ -1,15 +0,0 @@
#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);
-94
View File
@@ -1,94 +0,0 @@
#include "lib_standard.h"
#include "toy_memory.h"
#include <time.h>
#include <sys/time.h>
static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to clock\n");
return -1;
}
//get the time from C (what a pain)
time_t rawtime = time(NULL);
struct tm* timeinfo = localtime( &rawtime );
char* timestr = asctime(timeinfo);
//push to the stack
int len = strlen(timestr) - 1; //-1 for the newline
Toy_Literal timeLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(timestr, len));
//push to the stack
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
//cleanup
Toy_freeLiteral(timeLiteral);
return 1;
}
//call the hook
typedef struct Natives {
char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"clock", nativeClock},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
-411
View File
@@ -1,411 +0,0 @@
#include "lib_timer.h"
#include "toy_memory.h"
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
//normallize
if (x->tv_usec > 999999) {
x->tv_sec += x->tv_usec / 1000000;
x->tv_usec %= 1000000;
}
if (y->tv_usec > 999999) {
y->tv_sec += y->tv_usec / 1000000;
y->tv_usec %= 1000000;
}
//calc
result->tv_sec = x->tv_sec - y->tv_sec;
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
if (result->tv_sec != 0) { //only works far from 0
result->tv_usec += 1000000;
result->tv_sec--; // borrow
}
}
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
}
//god damn it
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
struct timeval* d = TOY_ALLOCATE(struct timeval, 1);
//I gave up, copied from SO
timeval_subtract(d, rhs, lhs);
return d;
}
//callbacks
static int nativeStartTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
gettimeofday(timeinfo, NULL);
//wrap in an opaque literal for Toy
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
Toy_freeLiteral(timeLiteral);
return 1;
}
static int nativeStopTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval timerStop;
gettimeofday(&timerStop, NULL);
//unwrap the opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timerStart = TOY_AS_OPAQUE(timeLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(timerStart, &timerStop);
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(diffLiteral);
return 1;
}
static int nativeCreateTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
return -1;
}
//get the args
Toy_Literal microsecondLiteral = Toy_popLiteralArray(arguments);
Toy_Literal secondLiteral = Toy_popLiteralArray(arguments);
Toy_Literal secondLiteralIdn = secondLiteral;
if (TOY_IS_IDENTIFIER(secondLiteral) && Toy_parseIdentifierToValue(interpreter, &secondLiteral)) {
Toy_freeLiteral(secondLiteralIdn);
}
Toy_Literal microsecondLiteralIdn = microsecondLiteral;
if (TOY_IS_IDENTIFIER(microsecondLiteral) && Toy_parseIdentifierToValue(interpreter, &microsecondLiteral)) {
Toy_freeLiteral(microsecondLiteralIdn);
}
if (!TOY_IS_INTEGER(secondLiteral) || !TOY_IS_INTEGER(microsecondLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return -1;
}
if (TOY_AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || TOY_AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (TOY_AS_INTEGER(secondLiteral) != 0 && TOY_AS_INTEGER(microsecondLiteral) < 0) ) {
interpreter->errorOutput("Microseconds out of range in createTimer\n");
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return -1;
}
//get the timeinfo from toy
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
timeinfo->tv_sec = TOY_AS_INTEGER(secondLiteral);
timeinfo->tv_usec = TOY_AS_INTEGER(microsecondLiteral);
//wrap in an opaque literal for Toy
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(secondLiteral);
Toy_freeLiteral(microsecondLiteral);
return 1;
}
static int nativeGetTimerSeconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
return -1;
}
//unwrap the opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the result literal
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_sec);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(result);
return 1;
}
static int nativeGetTimerMicroseconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
return -1;
}
//unwrap the opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the result literal
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_usec);
Toy_pushLiteralArray(&interpreter->stack, result);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(result);
return 1;
}
static int nativeCompareTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
return -1;
}
//unwrap the opaque literals
Toy_Literal rhsLiteral = Toy_popLiteralArray(arguments);
Toy_Literal lhsLiteral = Toy_popLiteralArray(arguments);
Toy_Literal lhsLiteralIdn = lhsLiteral;
if (TOY_IS_IDENTIFIER(lhsLiteral) && Toy_parseIdentifierToValue(interpreter, &lhsLiteral)) {
Toy_freeLiteral(lhsLiteralIdn);
}
Toy_Literal rhsLiteralIdn = rhsLiteral;
if (TOY_IS_IDENTIFIER(rhsLiteral) && Toy_parseIdentifierToValue(interpreter, &rhsLiteral)) {
Toy_freeLiteral(rhsLiteralIdn);
}
if (!TOY_IS_OPAQUE(lhsLiteral) || !TOY_IS_OPAQUE(rhsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
Toy_freeLiteral(lhsLiteral);
Toy_freeLiteral(rhsLiteral);
return -1;
}
struct timeval* lhsTimer = TOY_AS_OPAQUE(lhsLiteral);
struct timeval* rhsTimer = TOY_AS_OPAQUE(rhsLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(lhsTimer, rhsTimer);
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
Toy_freeLiteral(lhsLiteral);
Toy_freeLiteral(rhsLiteral);
Toy_freeLiteral(diffLiteral);
return 1;
}
static int nativeTimerToString(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
return -1;
}
//unwrap in an opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
//create the string literal
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
char buffer[128];
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
}
else { //normal case
char buffer[128];
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
}
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
Toy_freeLiteral(timeLiteral);
Toy_freeLiteral(resultLiteral);
return 1;
}
static int nativeDestroyTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
return -1;
}
//unwrap in an opaque literal
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
Toy_Literal timeLiteralIdn = timeLiteral;
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
Toy_freeLiteral(timeLiteralIdn);
}
if (!TOY_IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
Toy_freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
TOY_FREE(struct timeval, timer);
Toy_freeLiteral(timeLiteral);
return 0;
}
//call the hook
typedef struct Natives {
char* name;
Toy_NativeFn fn;
} Natives;
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
//build the natives list
Natives natives[] = {
{"startTimer", nativeStartTimer},
{"_stopTimer", nativeStopTimer},
{"createTimer", nativeCreateTimer},
{"_getTimerSeconds", nativeGetTimerSeconds},
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
{"_compareTimer", nativeCompareTimer},
{"_timerToString", nativeTimerToString},
{"_destroyTimer", nativeDestroyTimer},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!TOY_IS_NULL(alias)) {
//make sure the name isn't taken
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
Toy_freeLiteral(alias);
return -1;
}
//create the dictionary to load up with functions
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
Toy_initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
Toy_Literal func = TOY_TO_FUNCTION_NATIVE_LITERAL(natives[i].fn);
Toy_setLiteralDictionary(dictionary, name, func);
Toy_freeLiteral(name);
Toy_freeLiteral(func);
}
//build the type
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
Toy_declareScopeVariable(interpreter->scope, alias, type);
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
Toy_freeLiteral(dict);
Toy_freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "toy_interpreter.h"
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
+517
View File
@@ -0,0 +1,517 @@
#include "ast_inspector.h"
#include "bytecode_inspector.h"
#include "bucket_inspector.h"
#include "toy_console_colors.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_vm.h"
//NOTE: for testing
#include "standard_library.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned char* readFile(char* path, int* size) {
//open the file
FILE* file = fopen(path, "rb");
if (file == NULL) {
*size = -1; //missing file error
return NULL;
}
//determine the file's length
fseek(file, 0L, SEEK_END);
*size = ftell(file);
rewind(file);
//make some space
unsigned char* buffer = malloc(*size + 1);
if (buffer == NULL) {
fclose(file);
return NULL;
}
//read the file
if (fread(buffer, sizeof(unsigned char), *size, file) < (unsigned int)(*size)) {
fclose(file);
free(buffer);
*size = -2; //singal a read error
return NULL;
}
buffer[(*size)++] = '\0';
//clean up and return
fclose(file);
return buffer;
}
int getFileName(char* dest, const char* src, size_t destLength) {
#define MIN(a, b) ((a) < (b) ? (a) : (b))
char* p = NULL;
//find the last slash, regardless of platform
p = strrchr(src, '\\');
if (p == NULL) {
p = strrchr(src, '/');
}
if (p == NULL) {
int len = MIN(strlen(src), destLength-1);
strncpy(dest, src, len);
dest[len] = '\0';
return len;
}
p++; //skip the slash
//determine length of the file name
int len = MIN(strlen(src), destLength-1);
//copy to the dest
strncpy(dest, p, len);
dest[len] = '\0';
return len;
#undef MIN
}
//error callbacks
static int errorAndExitCallback(const char* msg) {
fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
exit(-1);
}
static int errorAndContinueCallback(const char* msg) {
return fprintf(stderr, TOY_CC_ERROR "Error: %s" TOY_CC_RESET "\n", msg);
}
static int assertFailureAndExitCallback(const char* msg) {
fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
exit(-1);
}
static int assertFailureAndContinueCallback(const char* msg) {
return fprintf(stderr, TOY_CC_ASSERT "Assert Failure: %s" TOY_CC_RESET "\n", msg);
}
static int noOpCallback(const char* msg) {
//NO-OP
(void)msg;
return 0;
}
static int silentExitCallback(const char* msg) {
//NO-OP
(void)msg;
exit(-1);
}
//handle command line arguments
typedef struct CmdLine {
bool error;
bool help;
bool version;
char* infile;
int infileLength;
bool silentPrint;
bool silentAssert;
bool removeAssert;
bool verbose;
} CmdLine;
void usageCmdLine(int argc, const char* argv[]) {
(void)argc;
printf("Usage: %s [ -h | -v | -f source.toy ]\n\n", argv[0]);
}
void helpCmdLine(int argc, const char* argv[]) {
usageCmdLine(argc, argv);
printf("The Toy Programming Language, leave arguments blank for an interactive REPL.\n\n");
printf(" -h, --help\t\t\tShow this help then exit.\n");
printf(" -v, --version\t\t\tShow version and copyright information then exit.\n");
printf(" -f, --file infile\t\tParse, compile and execute the source file then exit.\n");
printf(" --silent-print\t\tSuppress output from the print keyword.\n");
printf(" --silent-assert\t\tSuppress output from the assert keyword.\n");
printf(" --remove-assert\t\tDo not include the assert statement in the bytecode.\n");
printf(" -d, --verbose\t\tPrint debugging information about Toy's internals.\n");
}
void versionCmdLine(int argc, const char* argv[]) {
(void)argc;
(void)argv;
printf("The Toy Programming Language, Version %d.%d.%d %s\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
//copy/pasted from the license file - there's a way to include it directly, but it's too finnicky to bother
const char* license =
"Copyright (c) 2020-2026 Kayne Ruse, KR Game Studios\n"
"\n"
"This software is provided 'as-is', without any express or implied\n"
"warranty. In no event will the authors be held liable for any damages\n"
"arising from the use of this software.\n"
"\n"
"Permission is granted to anyone to use this software for any purpose,\n"
"including commercial applications, and to alter it and redistribute it\n"
"freely, subject to the following restrictions:\n"
"\n"
"1. The origin of this software must not be misrepresented; you must not\n"
"claim that you wrote the original software. If you use this software\n"
"in a product, an acknowledgment in the product documentation would be\n"
"appreciated but is not required.\n"
"2. Altered source versions must be plainly marked as such, and must not be\n"
"misrepresented as being the original software.\n"
"3. This notice may not be removed or altered from any source distribution.\n"
"\n"
;
printf("%s",license);
}
CmdLine parseCmdLine(int argc, const char* argv[]) {
CmdLine cmd = {
.error = false,
.help = false,
.version = false,
.infile = NULL,
.infileLength = 0,
.silentPrint = false,
.silentAssert = false,
.removeAssert = false,
.verbose = false,
};
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
cmd.help = true;
}
else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
cmd.version = true;
}
else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) {
if (argc <= i + 1) {
cmd.error = true;
}
else {
if (cmd.infile != NULL) { //don't leak
free(cmd.infile);
}
i++;
//total space to reserve
cmd.infileLength = strlen(argv[i]) + 1;
cmd.infileLength = (cmd.infileLength + 3) & ~3; //BUGFIX: align to word size
cmd.infile = malloc(cmd.infileLength);
if (cmd.infile == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space while parsing the command line, exiting\n" TOY_CC_RESET);
exit(-1);
}
int len = strlen(argv[i]);
strncpy(cmd.infile, argv[i], len);
cmd.infile[len] = '\0';
}
}
else if (!strcmp(argv[i], "--silent-print")) {
cmd.silentPrint = true;
}
else if (!strcmp(argv[i], "--silent-assert")) {
cmd.silentAssert = true;
}
else if (!strcmp(argv[i], "--remove-assert")) {
cmd.removeAssert = true;
}
else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--verbose")) {
cmd.verbose = true;
}
else {
cmd.error = true;
}
}
return cmd;
}
//debugging
static void debugStackPrint(Toy_Stack* stack) {
//DEBUG: if there's anything on the stack, print it
if (stack->count > 0) {
Toy_Bucket* stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
printf("\n" TOY_CC_NOTICE "Stack Dump" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s" TOY_CC_RESET "\n", "type", "value");
for (unsigned int i = 0; i < stack->count; i++) {
Toy_Value v = ((Toy_Value*)(stack + 1))[i]; //'stack + 1' is a naughty trick
//print type
printf("%-20s", Toy_getValueTypeAsCString(v.type));
//print value
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
char* buffer = Toy_getStringRaw(string);
printf("%-20s", buffer);
free(buffer);
Toy_freeString(string);
printf("\n");
}
Toy_freeBucket(&stringBucket);
}
}
static void debugScopePrint(Toy_Scope* scope, int depth) {
//DEBUG: if there's anything in the scope, print it
if (scope->count > 0) {
Toy_Bucket* stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
printf("\n" TOY_CC_NOTICE "Scope Dump [%d]" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s%-20s" TOY_CC_RESET "\n", depth, "type", "name", "value");
for (unsigned int i = 0; i < scope->capacity; i++) {
if (scope->data[i].key == NULL || scope->data[i].key->info.length == 0) {
continue;
}
Toy_String* k = scope->data[i].key;
Toy_Value v = scope->data[i].value;
printf("%-10s%-10s%-20s", Toy_getValueTypeAsCString(scope->data[i].type), scope->data[i].constant ? "const" : "", k != NULL ? k->leaf.data : "");
//print value
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
char* buffer = Toy_getStringRaw(string);
printf("%-20s", buffer);
free(buffer);
Toy_freeString(string);
printf("\n");
}
Toy_freeBucket(&stringBucket);
}
if (scope->next != NULL) {
debugScopePrint(scope->next, depth + 1);
}
}
//repl function
int repl(const char* filepath, bool verbose) {
//output options
Toy_setPrintCallback(puts);
Toy_setErrorCallback(errorAndContinueCallback);
Toy_setAssertFailureCallback(assertFailureAndContinueCallback);
//vars to use
char prompt[256];
getFileName(prompt, filepath, 256);
unsigned int INPUT_BUFFER_SIZE = 4096;
char inputBuffer[INPUT_BUFFER_SIZE];
memset(inputBuffer, 0, INPUT_BUFFER_SIZE);
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); //TODO: gc this
Toy_VM vm;
Toy_initVM(&vm);
printf("%s> ", prompt); //shows the terminal prompt and begin
//read from the terminal
while(fgets(inputBuffer, INPUT_BUFFER_SIZE, stdin)) {
//work around fgets() adding a newline
unsigned int length = strlen(inputBuffer);
if (inputBuffer[length - 1] == '\n') {
inputBuffer[--length] = '\0';
}
if (length == 0 || !inputBuffer[ strspn(inputBuffer, " \r\n\t") ]) {
printf("%s> ", prompt); //shows the terminal prompt and restart
continue;
}
//end
if (strlen(inputBuffer) == 4 && (strncmp(inputBuffer, "exit", 4) == 0 || strncmp(inputBuffer, "quit", 4) == 0)) {
break;
}
//parse the input, prep the VM for execution
Toy_Lexer lexer;
Toy_bindLexer(&lexer, inputBuffer);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser); //Ast is in the bucket, so it doesn't need to be freed
//parsing error, retry
if (parser.error) {
printf("%s> ", prompt); //shows the terminal prompt
continue;
}
if (verbose) {
inspect_ast(ast);
}
unsigned char* bytecode = Toy_compileToBytecode(ast);
if (verbose) {
inspect_bytecode(bytecode);
}
//WARN: Hacky debugging
if (vm.scope == NULL) {
Toy_bindVM(&vm, bytecode, NULL);
initStandardLibrary(&vm);
}
else {
Toy_bindVM(&vm, bytecode, NULL);
}
//run
Toy_runVM(&vm);
int depthBeforeGC = 0;
int depthAfterGC = 0;
//print the debug info
if (verbose) {
debugStackPrint(vm.stack);
debugScopePrint(vm.scope, 0);
depthBeforeGC = inspect_bucket(&vm.memoryBucket);
}
//free the memory, and leave the VM ready for the next loop
Toy_resetVM(&vm, true, true);
if (verbose) {
depthAfterGC = inspect_bucket(&vm.memoryBucket);
printf("GC Report: %d -> %d\n", depthBeforeGC, depthAfterGC);
}
free(bytecode);
printf("%s> ", prompt); //shows the terminal prompt
}
//cleanup all memory
Toy_freeVM(&vm);
Toy_freeBucket(&bucket);
return 0;
}
//main file
int main(int argc, const char* argv[]) {
Toy_setPrintCallback(puts);
Toy_setErrorCallback(errorAndExitCallback);
Toy_setAssertFailureCallback(assertFailureAndExitCallback);
//if there's args, process them
CmdLine cmd = parseCmdLine(argc, argv);
//output options
if (cmd.silentPrint) {
Toy_setPrintCallback(noOpCallback);
}
if (cmd.silentAssert) {
Toy_setAssertFailureCallback(silentExitCallback);
}
//process
if (cmd.error) {
usageCmdLine(argc, argv);
}
else if (cmd.help) {
helpCmdLine(argc, argv);
}
else if (cmd.version) {
versionCmdLine(argc, argv);
}
else if (cmd.infile != NULL) {
//read the source file
int size;
unsigned char* source = readFile(cmd.infile, &size);
//check the file
if (source == NULL) {
if (size == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Could not parse an empty file '%s', exiting\n" TOY_CC_RESET, cmd.infile);
return -1;
}
else if (size == -1) {
fprintf(stderr, TOY_CC_ERROR "ERROR: File not found '%s', exiting\n" TOY_CC_RESET, cmd.infile);
return -1;
}
else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown error while reading file '%s', exiting\n" TOY_CC_RESET, cmd.infile);
return -1;
}
}
free(cmd.infile);
cmd.infile = NULL;
cmd.infileLength = 0;
//compile the source code
Toy_Lexer lexer;
Toy_bindLexer(&lexer, (char*)source);
Toy_Parser parser;
Toy_bindParser(&parser, &lexer);
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
if (cmd.verbose) {
inspect_ast(ast);
}
unsigned char* bytecode = Toy_compileToBytecode(ast);
Toy_freeBucket(&bucket);
free(source);
if (cmd.verbose) {
inspect_bytecode(bytecode);
}
//run the compiled code
Toy_VM vm;
Toy_initVM(&vm);
Toy_bindVM(&vm, bytecode, NULL);
initStandardLibrary(&vm); //WARN: Hacky debugging
Toy_runVM(&vm);
//print the debug info
if (cmd.verbose) {
debugStackPrint(vm.stack);
debugScopePrint(vm.scope, 0);
}
//cleanup
Toy_freeVM(&vm);
free(bytecode);
}
else {
repl(argv[0], cmd.verbose);
}
return 0;
}
+54 -27
View File
@@ -1,36 +1,63 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm -lToy
LDFLAGS+=-Wl,-rpath,'$$ORIGIN'
IDIR+=. ../source
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=-ltoy
ODIR = obj
SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
OUTNAME=toy
OUT=../$(TOY_OUTDIR)/toyrepl
all: $(OBJ)
ifeq ($(shell uname),Darwin)
cp $(PWD)/$(TOY_OUTDIR)/lib$(OUTNAME).dylib /usr/local/lib/
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) $(LIBS)
else
$(CC) -DTOY_IMPORT $(CFLAGS) -o $(OUT) $(OBJ) -Wl,-rpath,. -L$(realpath $(shell pwd)/../$(TOY_OUTDIR)) $(LIBS)
ifeq ($(shell uname),Darwin) #make sure there's enough space for the dylib fix
LDFLAGS+=-Wl,-headerpad_max_install_names
endif
release: all
strip $(OUT)
$(OBJ): | $(ODIR)
#directories
REPL_ROOTDIR=..
REPL_REPLDIR=.
REPL_SOURCEDIR=$(REPL_ROOTDIR)/$(TOY_SOURCEDIR)
$(ODIR):
mkdir $(ODIR)
REPL_OUTDIR=$(REPL_ROOTDIR)/$(TOY_OUTDIR)
REPL_OBJDIR=$(TOY_OBJDIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
#file names
REPL_REPLFILES=$(wildcard $(REPL_REPLDIR)/*.c)
REPL_OBJFILES=$(addprefix $(REPL_OBJDIR)/,$(notdir $(REPL_REPLFILES:.c=.o)))
REPL_TARGETNAME=repl
.PHONY: clean
#file extensions
ifeq ($(OS),Windows_NT)
REPL_TARGETEXT=.exe
else
REPL_TARGETEXT=.out
endif
clean:
$(RM) $(ODIR)
rm /usr/local/lib/lib$(OUTNAME).dylib
#linker fix
LDFLAGS+=-L$(realpath $(REPL_OUTDIR))
#build the object files, compile the test cases, and run
all: build link
#targets for each step
.PHONY: build
build: $(REPL_OBJDIR) $(REPL_OBJFILES)
.PHONY: link
link: $(REPL_OUTDIR) $(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT)
#util targets
$(REPL_OUTDIR):
mkdir $(REPL_OUTDIR)
$(REPL_OBJDIR):
mkdir $(REPL_OBJDIR)
#compilation steps
$(REPL_OBJDIR)/%.o: $(REPL_REPLDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(REPL_REPLDIR)) $(addprefix -I,$(REPL_SOURCEDIR)) $(CFLAGS)
$(REPL_OUTDIR)/$(REPL_TARGETNAME)$(REPL_TARGETEXT): $(REPL_OBJFILES)
$(CC) -DTOY_IMPORT $(CFLAGS) -o $@ $(REPL_OBJFILES) $(LDFLAGS) $(LIBS)
ifeq ($(shell uname),Darwin) #dylib fix
otool -L $@
install_name_tool -add_rpath @executable_path/. $@
install_name_tool -change $(REPL_OUTDIR)/libToy.dylib @executable_path/libToy.dylib $@
otool -L $@
endif
-183
View File
@@ -1,183 +0,0 @@
#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 "toy_console_colors.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void repl() {
//repl does it's own thing for now
bool error = false;
const int size = 2048;
char input[size];
memset(input, 0, size);
Toy_Interpreter interpreter; //persist the interpreter for the scopes
Toy_initInterpreter(&interpreter);
//inject the libs
Toy_injectNativeHook(&interpreter, "about", Toy_hookAbout);
Toy_injectNativeHook(&interpreter, "compound", Toy_hookCompound);
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
for(;;) {
printf("> ");
//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))) {
break;
}
//setup this iteration
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
Toy_initLexer(&lexer, input);
Toy_private_setComments(&lexer, false); //BUGFIX: disable comments here
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//run this iteration
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and restart
if (node->type == TOY_AST_NODE_ERROR) {
if (Toy_commandLine.verbose) {
printf(TOY_CC_ERROR "Error node detected\n" TOY_CC_RESET);
}
error = true;
Toy_freeASTNode(node);
break;
}
Toy_writeCompiler(&compiler, node);
Toy_freeASTNode(node);
node = Toy_scanParser(&parser);
}
if (!error) {
//get the bytecode dump
int size = 0;
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
//run the bytecode
Toy_runInterpreter(&interpreter, tb, size);
}
//clean up this iteration
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
error = false;
}
Toy_freeInterpreter(&interpreter);
}
//entry point
int main(int argc, const char* argv[]) {
Toy_initCommandLine(argc, argv);
//lib setup (hacky - only really for this program)
Toy_initDriveDictionary();
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
Toy_freeLiteral(driveLiteral);
Toy_freeLiteral(pathLiteral);
//command line specific actions
if (Toy_commandLine.error) {
Toy_usageCommandLine(argc, argv);
return 0;
}
if (Toy_commandLine.help) {
Toy_helpCommandLine(argc, argv);
return 0;
}
if (Toy_commandLine.version) {
Toy_copyrightCommandLine(argc, argv);
return 0;
}
//version
if (Toy_commandLine.verbose) {
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d, built '%s'\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
}
//run source file
if (Toy_commandLine.sourcefile) {
Toy_runSourceFile(Toy_commandLine.sourcefile);
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
//run from stdin
if (Toy_commandLine.source) {
Toy_runSource(Toy_commandLine.source);
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
//compile source file
if (Toy_commandLine.compilefile && Toy_commandLine.outfile) {
size_t size = 0;
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;
}
Toy_writeFile(Toy_commandLine.outfile, tb, size);
return 0;
}
//run binary
if (Toy_commandLine.binaryfile) {
Toy_runBinaryFile(Toy_commandLine.binaryfile);
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
repl();
//lib cleanup
Toy_freeDriveDictionary();
return 0;
}
-153
View File
@@ -1,153 +0,0 @@
#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 "toy_console_colors.h"
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_compiler.h"
#include "toy_interpreter.h"
#include <stdio.h>
#include <stdlib.h>
//IO functions
const char* Toy_readFile(const char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
if (file == NULL) {
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
return NULL;
}
fseek(file, 0L, SEEK_END);
*fileSize = ftell(file);
rewind(file);
char* buffer = (char*)malloc(*fileSize + 1);
if (buffer == NULL) {
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
return NULL;
}
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
if (bytesRead < *fileSize) {
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
return NULL;
}
fclose(file);
return buffer;
}
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size) {
FILE* file = fopen(path, "wb");
if (file == NULL) {
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
return -1;
}
int written = fwrite(bytes, size, 1, file);
if (written != 1) {
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
return -1;
}
fclose(file);
return 0;
}
//repl functions
const unsigned char* Toy_compileString(const char* source, size_t* size) {
Toy_Lexer lexer;
Toy_Parser parser;
Toy_Compiler compiler;
Toy_initLexer(&lexer, source);
Toy_initParser(&parser, &lexer);
Toy_initCompiler(&compiler);
//run the parser until the end of the source
Toy_ASTNode* node = Toy_scanParser(&parser);
while(node != NULL) {
//pack up and leave
if (node->type == TOY_AST_NODE_ERROR) {
Toy_freeASTNode(node);
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
return NULL;
}
Toy_writeCompiler(&compiler, node);
Toy_freeASTNode(node);
node = Toy_scanParser(&parser);
}
//get the bytecode dump
const unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
//cleanup
Toy_freeCompiler(&compiler);
Toy_freeParser(&parser);
//no lexer to clean up
//finally
return tb;
}
void Toy_runBinary(const unsigned char* tb, size_t size) {
Toy_Interpreter interpreter;
Toy_initInterpreter(&interpreter);
//inject the libs
Toy_injectNativeHook(&interpreter, "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);
Toy_runInterpreter(&interpreter, tb, size);
Toy_freeInterpreter(&interpreter);
}
void Toy_runBinaryFile(const char* fname) {
size_t size = 0; //not used
const unsigned char* tb = (const unsigned char*)Toy_readFile(fname, &size);
if (!tb) {
return;
}
Toy_runBinary(tb, size);
//interpreter takes ownership of the binary data
}
void Toy_runSource(const char* source) {
size_t size = 0;
const unsigned char* tb = Toy_compileString(source, &size);
if (!tb) {
return;
}
Toy_runBinary(tb, size);
}
void Toy_runSourceFile(const char* fname) {
size_t size = 0; //not used
const char* source = Toy_readFile(fname, &size);
if (!source) {
return;
}
Toy_runSource(source);
free((void*)source);
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "toy_common.h"
const char* Toy_readFile(const char* path, size_t* fileSize);
int Toy_writeFile(const char* path, const unsigned char* bytes, size_t size);
const unsigned char* Toy_compileString(const char* source, size_t* size);
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);
+64
View File
@@ -0,0 +1,64 @@
#include "standard_library.h"
#include "toy_console_colors.h"
#include "toy_print.h"
#include "toy_scope.h"
#include "toy_stack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct CallbackPairs {
const char* name;
Toy_nativeCallback callback;
} CallbackPairs;
//example callbacks
void debug(Toy_VM* vm) {
(void)vm;
Toy_print("This function returns the integer '42' to the calling scope.");
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(42));
}
void identity(Toy_VM* vm) {
//does nothing, but any arguements are left on the stack as results
(void)vm;
}
void echo(Toy_VM* vm) {
//pops one argument, and prints it
Toy_Value value = Toy_popStack(&vm->stack);
Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value);
char* cstr = Toy_getStringRaw(string);
Toy_print(cstr);
free(cstr);
Toy_freeString(string);
Toy_freeValue(value);
}
CallbackPairs callbackPairs[] = {
{"debug", debug},
{"identity", identity},
{"echo", echo},
{NULL, NULL},
};
//exposed functions
void initStandardLibrary(Toy_VM* vm) {
if (vm == NULL || vm->scope == NULL || vm->memoryBucket == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't initialize standard library, exiting\n" TOY_CC_RESET);
exit(-1);
}
//declare each pair
for (int i = 0; callbackPairs[i].name; i++) {
Toy_String* key = Toy_createStringLength(&vm->memoryBucket, callbackPairs[i].name, strlen(callbackPairs[i].name));
Toy_Function* fn = Toy_createFunctionFromCallback(&(vm->memoryBucket), callbackPairs[i].callback);
Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true);
}
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "toy_vm.h"
void initStandardLibrary(Toy_VM*);
+16
View File
@@ -0,0 +1,16 @@
//calculate the nth fibonacci number, and print it
var counter: Int = 0;
var first: Int = 1;
var second: Int = 0;
while (counter < 100_000) {
var third: Int = first + second;
first = second;
second = third;
print third;
++counter;
}
+11
View File
@@ -0,0 +1,11 @@
fn output(arg) {
print arg;
}
var array = ["alpha", "bravo", "charlie"];
array.forEach(echo);
array.forEach(output);
-125
View File
@@ -1,125 +0,0 @@
import node;
//constants
var SPEED: int const = 10;
//variables
var parent: opaque = null;
var posX: int = 50;
var posY: int = 50;
var WIDTH: int const = 100;
var HEIGHT: int const = 100;
var xspeed: int = 0;
var yspeed: int = 0;
//accessors - variables are private, functions are public
fn getX(node: opaque) {
return posX;
}
fn getY(node: opaque) {
return posY;
}
//lifecycle functions
fn onInit(node: opaque) {
print "render.toy:onInit() called\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";
}
-89
View File
@@ -1,89 +0,0 @@
//single line comment
/*
multi line comment
*/
//test primitive literals
print "hello world";
print null;
print true;
print false;
print 42;
print 3.14;
print -69;
print -4.20;
print 2 + (3 * 3);
//test operators (integers)
print 1 + 1;
print 1 - 1;
print 2 * 2;
print 1 / 2;
print 4 % 2;
//test operators (floats)
print 1.0 + 1.0;
print 1.0 - 1.0;
print 2.0 * 2.0;
print 1.0 / 2.0;
//test scopes
{
print "This statement is within a scope.";
{
print "This is a deeper scope.";
}
}
print "Back to the outer scope.";
//test scope will delegate to higher scope
var a = 1;
{
a = 2;
print a;
}
print a;
//test scope will shadow higher scope on redefine
var b: int = 3;
{
var b = 4;
print b;
}
print b;
//test compounds, repeatedly
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
//test empties
print [];
print [:];
//test nested compounds
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
//var declarations
var x = 31;
var y : int = 42;
var arr : [int] = [1, 2, 3, 42];
var dict : [string:int] = ["hello": 1, "world":2];
//printing expressions
print x;
print x + y;
print arr;
print dict;
//test asserts at the end of the file
assert x, "This won't be seen";
assert true, "This won't be seen";
assert false, "This is a failed assert, and will end execution";
print "This will not be printed because of the above assert";
-21
View File
@@ -1,21 +0,0 @@
//memoize the fib function
var memo: [int : int] = [:];
fn fib(n : int) {
if (n < 2) {
return n;
}
var result = memo[n];
if (result == null) {
result = fib(n-1) + fib(n-2);
memo[n] = result;
}
return result;
}
for (var i = 0; i < 40; i++) {
var res = fib(i);
print string i + ": " + string res + "\n";
}
+9 -5
View File
@@ -1,9 +1,13 @@
fn fib(n : int) {
//tentatively functional
//fibonacci sequence
fn fib(n) {
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 + "\n";
}
print fib(12);
//Note to my future self: yes, the base case in 'fib()' is 'n < 2', stop second guessing yourself!
//Note to my past self: don't tell me what to do!
//Note to both of you: keep it down you young whipper snappers!
+24
View File
@@ -0,0 +1,24 @@
//standard example, using 'while' instead of 'for', because it's not ready yet
var counter: Int = 0;
while (++counter <= 100) {
var result: String = "";
if (counter % 3 == 0) {
result = result .. "fizz";
}
if (counter % 5 == 0) {
result = result .. "buzz";
}
//finally
if (result != "") {
print result;
}
else {
print counter;
}
}
+19
View File
@@ -0,0 +1,19 @@
/*
fn swap(a, b) {
return b, a;
}
var a = 42;
var b = 69;
var c;
var d;
//BUG: still causes a segfault
c, d = swap(a, b);
*/
{ var str = "Hello"; var i = 0; while (i < 100) { str = str .. " World"; i++; } }
+19
View File
@@ -0,0 +1,19 @@
//find the leap years
fn isLeapYear(n: Int) {
if (n % 400 == 0) return true;
if (n % 100 == 0) return false;
return n % 4 == 0;
}
//check for string reuse
{
print isLeapYear(1999);
}
{
print isLeapYear(2000);
}
{
print isLeapYear(2004);
}
-61
View File
@@ -1,61 +0,0 @@
//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();
}
+12
View File
@@ -0,0 +1,12 @@
var randi: Int = 69420;
fn rand() {
return randi = randi * 1664525 + 1013904223;
}
var a = rand();
-49
View File
@@ -1,49 +0,0 @@
//number of iterations
var SIZE: int const = 260;
//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";
//run
for (var iteration = 0; iteration < SIZE -1; iteration++) {
//left
var output = (lookup[" "][prev[0]][prev[1]]);
//middle
for (var i = 1; i < SIZE-1; i++) {
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
}
//right
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
print output + "\n";
prev = output;
}
-10
View File
@@ -1,10 +0,0 @@
var xrel: int = 0;
var yrel: int = 0;
if (xrel > 1 || xrel < -1 || yrel > 1 || yrel < -1) {
print "outside";
}
else {
print "inside";
}
+21
View File
@@ -0,0 +1,21 @@
fn makeCounter() {
var counter: Int = 0;
fn increment() {
return ++counter;
}
return increment;
}
var tally = makeCounter();
while (true) {
var result = tally();
print result;
if (result >= 10) {
break;
}
}
+42 -37
View File
@@ -1,54 +1,59 @@
#compiler settings
CC=gcc
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof
LIBS+=-lm
LDFLAGS+=
IDIR+=.
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
LIBS+=
#directories
SRC_ROOTDIR=..
SRC_SOURCEDIR=.
ODIR = obj
SRC = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
SRC_OUTDIR=$(SRC_ROOTDIR)/$(TOY_OUTDIR)
SRC_OBJDIR=$(TOY_OBJDIR)
OUTNAME=toy
#file names
SRC_SOURCEFILES=$(wildcard $(SRC_SOURCEDIR)/*.c)
SRC_OBJFILES=$(addprefix $(SRC_OBJDIR)/,$(notdir $(SRC_SOURCEFILES:.c=.o)))
SRC_TARGETNAME=Toy
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
else ifeq ($(shell uname),Linux)
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
CFLAGS += -fPIC
#SRC_LIBLINE is a fancy way of making the linker work correctly
ifeq ($(shell uname),Linux)
SRC_TARGETEXT=.so
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive
CFLAGS+=-fPIC
else ifeq ($(shell uname),NetBSD)
SRC_TARGETEXT=.so
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive
CFLAGS+=-fPIC
else ifeq ($(OS),Windows_NT)
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
SRC_TARGETEXT=.dll
SRC_LIBLINE=-shared -Wl,-rpath,. -Wl,--out-implib=$(SRC_OUTDIR)/lib$(SRC_TARGETNAME).a -Wl,--whole-archive $(SRC_OBJFILES) -Wl,--no-whole-archive -Wl,--export-all-symbols -Wl,--enable-auto-import
else ifeq ($(shell uname),Darwin)
LIBLINE = $(OBJ)
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).dylib
SRC_TARGETEXT=.dylib
SRC_LIBLINE=-shared -Wl,-rpath,. $(SRC_OBJFILES)
else
@echo "Platform test failed - what platform is this?"
exit 1
endif
library: $(OBJ)
$(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE)
#build the object files, compile the test cases, and run
all: build link
static: $(OBJ)
ar crs ../$(TOY_OUTDIR)/lib$(OUTNAME).a $(OBJ)
#targets for each step
.PHONY: build
build: $(SRC_OUTDIR) $(SRC_OBJDIR) $(SRC_OBJFILES)
library-release: $(OBJ) library
strip $(OUT)
.PHONY: link
link: $(SRC_OUTDIR)
$(CC) -DTOY_EXPORT $(CFLAGS) -o $(SRC_OUTDIR)/lib$(SRC_TARGETNAME)$(SRC_TARGETEXT) $(SRC_LIBLINE)
static-release: $(OBJ) static
strip -d ../$(TOY_OUTDIR)/lib$(OUTNAME).a
#util targets
$(SRC_OUTDIR):
mkdir $(SRC_OUTDIR)
$(OBJ): | $(ODIR)
$(SRC_OBJDIR):
mkdir $(SRC_OBJDIR)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) $(ODIR)
#compilation steps
$(SRC_OBJDIR)/%.o: $(SRC_SOURCEDIR)/%.c
$(CC) -c -o $@ $< $(addprefix -I,$(SRC_SOURCEDIR)) $(CFLAGS)
+35
View File
@@ -0,0 +1,35 @@
#include "toy_array.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
//if some values will be removed, free them first
if (paramArray != NULL && paramArray->count > capacity) {
for (unsigned int i = capacity; i < paramArray->count; i++) {
Toy_freeValue(paramArray->data[i]);
}
}
//if you're freeing everything, just return
if (capacity == 0) {
free(paramArray);
return NULL;
}
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity;
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
if (array == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize a 'Toy_Array' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity);
exit(-1);
}
array->capacity = capacity;
array->count = paramArray == NULL ? 0 :
(array->count > capacity ? capacity : array->count); //truncate lost data
return array;
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "toy_common.h"
#include "toy_value.h"
//standard generic array
typedef struct Toy_Array { //32 | 64 BITNESS
unsigned int capacity; //4 | 4
unsigned int count; //4 | 4
Toy_Value data[]; //- | -
} Toy_Array; //8 | 8
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
//some useful sizes, could be swapped out as needed
#ifndef TOY_ARRAY_INITIAL_CAPACITY
#define TOY_ARRAY_INITIAL_CAPACITY 8
#endif
#ifndef TOY_ARRAY_EXPANSION_RATE
#define TOY_ARRAY_EXPANSION_RATE 2
#endif
+317
View File
@@ -0,0 +1,317 @@
#include "toy_ast.h"
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BLOCK;
tmp->block.innerScope = false;
tmp->block.child = NULL;
tmp->block.next = NULL;
tmp->block.tail = NULL;
(*astHandle) = tmp;
}
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child) {
//first, check if we're an empty head
if (block->block.child == NULL) {
block->block.child = child;
return; //NOTE: first call on an empty head skips any memory allocations
}
//run (or jump) until we hit the current tail
Toy_Ast* iter = block->block.tail ? block->block.tail : block;
while(iter->block.next != NULL) {
iter = iter->block.next;
}
//append a new link to the chain
Toy_private_initAstBlock(bucketHandle, &(iter->block.next));
//store the child in the new link, prep the tail pointer
iter->block.next->block.child = child;
block->block.tail = iter->block.next;
}
void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VALUE;
tmp->value.value = value;
(*astHandle) = tmp;
}
void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_UNARY;
tmp->unary.flag = flag;
tmp->unary.child = *astHandle;
(*astHandle) = tmp;
}
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BINARY;
tmp->binary.flag = flag;
tmp->binary.left = *astHandle; //left-recursive
tmp->binary.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BINARY_SHORT_CIRCUIT;
tmp->binary.flag = flag;
tmp->binary.left = *astHandle; //left-recursive
tmp->binary.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_COMPARE;
tmp->compare.flag = flag;
tmp->compare.left = *astHandle; //left-recursive
tmp->compare.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_GROUP;
tmp->group.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_COMPOUND;
tmp->compound.flag = flag;
tmp->compound.child = *astHandle;
(*astHandle) = tmp;
}
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_AGGREGATE;
tmp->aggregate.flag = flag;
tmp->aggregate.left = *astHandle; //left-recursive
tmp->aggregate.right = right;
(*astHandle) = tmp;
}
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ASSERT;
tmp->assert.child = child;
tmp->assert.message = msg;
(*astHandle) = tmp;
}
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_IF_THEN_ELSE;
tmp->ifThenElse.condBranch = condBranch;
tmp->ifThenElse.thenBranch = thenBranch;
tmp->ifThenElse.elseBranch = elseBranch;
(*astHandle) = tmp;
}
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_WHILE_THEN;
tmp->whileThen.condBranch = condBranch;
tmp->whileThen.thenBranch = thenBranch;
(*astHandle) = tmp;
}
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_BREAK;
(*astHandle) = tmp;
}
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_CONTINUE;
(*astHandle) = tmp;
}
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_RETURN;
tmp->fnReturn.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_PRINT;
tmp->print.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_DECLARE;
tmp->varDeclare.name = name;
tmp->varDeclare.valueType = valueType;
tmp->varDeclare.constant = constant;
tmp->varDeclare.expr = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_ASSIGN;
tmp->varAssign.flag = flag;
tmp->varAssign.target = (*astHandle);
tmp->varAssign.expr = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_ACCESS;
tmp->varAccess.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_FN_DECLARE;
tmp->fnDeclare.name = name;
tmp->fnDeclare.params = params;
tmp->fnDeclare.body = body;
(*astHandle) = tmp;
}
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* args) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_FN_INVOKE;
tmp->fnInvoke.function = (*astHandle);
tmp->fnInvoke.args = args;
(*astHandle) = tmp;
}
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ATTRIBUTE;
tmp->attribute.left = (*astHandle);
tmp->attribute.right = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_STACK_POP;
tmp->stackPop.child = (*astHandle);
(*astHandle) = tmp;
}
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_PASS;
(*astHandle) = tmp;
}
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ERROR;
(*astHandle) = tmp;
}
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_END;
(*astHandle) = tmp;
}
const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
switch(type) {
case TOY_AST_BLOCK: return "BLOCK";
case TOY_AST_VALUE: return "VALUE";
case TOY_AST_UNARY: return "UNARY";
case TOY_AST_BINARY: return "BINARY";
case TOY_AST_BINARY_SHORT_CIRCUIT: return "BINARY_SHORT_CIRCUIT";
case TOY_AST_COMPARE: return "COMPARE";
case TOY_AST_GROUP: return "GROUP";
case TOY_AST_COMPOUND: return "COMPOUND";
case TOY_AST_AGGREGATE: return "AGGREGATE";
case TOY_AST_ASSERT: return "ASSERT";
case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE";
case TOY_AST_WHILE_THEN: return "WHILE_THEN";
case TOY_AST_BREAK: return "BREAK";
case TOY_AST_CONTINUE: return "CONTINUE";
case TOY_AST_RETURN: return "RETURN";
case TOY_AST_PRINT: return "PRINT";
case TOY_AST_VAR_DECLARE: return "DECLARE";
case TOY_AST_VAR_ASSIGN: return "ASSIGN";
case TOY_AST_VAR_ACCESS: return "ACCESS";
case TOY_AST_FN_DECLARE: return "FN_DECLARE";
case TOY_AST_FN_INVOKE: return "FN_INVOKE";
case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
case TOY_AST_STACK_POP: return "STACK_POP";
case TOY_AST_PASS: return "PASS";
case TOY_AST_ERROR: return "ERROR";
case TOY_AST_END: return "END";
}
return NULL;
}
+311
View File
@@ -0,0 +1,311 @@
#pragma once
#include "toy_common.h"
#include "toy_bucket.h"
#include "toy_value.h"
#include "toy_string.h"
//each major type
typedef enum Toy_AstType {
TOY_AST_BLOCK,
TOY_AST_VALUE,
TOY_AST_UNARY,
TOY_AST_BINARY,
TOY_AST_BINARY_SHORT_CIRCUIT,
TOY_AST_COMPARE,
TOY_AST_GROUP,
TOY_AST_COMPOUND,
TOY_AST_AGGREGATE,
TOY_AST_ASSERT,
TOY_AST_IF_THEN_ELSE,
TOY_AST_WHILE_THEN,
TOY_AST_BREAK,
TOY_AST_CONTINUE,
TOY_AST_RETURN,
TOY_AST_PRINT,
TOY_AST_VAR_DECLARE,
TOY_AST_VAR_ASSIGN,
TOY_AST_VAR_ACCESS,
TOY_AST_FN_DECLARE,
TOY_AST_FN_INVOKE,
TOY_AST_ATTRIBUTE,
TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements
TOY_AST_PASS,
TOY_AST_ERROR,
TOY_AST_END,
} Toy_AstType;
//flags are handled differently by different types
typedef enum Toy_AstFlag {
TOY_AST_FLAG_NONE = 0,
//binary flags
TOY_AST_FLAG_ADD = 1,
TOY_AST_FLAG_SUBTRACT = 2,
TOY_AST_FLAG_MULTIPLY = 3,
TOY_AST_FLAG_DIVIDE = 4,
TOY_AST_FLAG_MODULO = 5,
TOY_AST_FLAG_AND = 6,
TOY_AST_FLAG_OR = 7,
TOY_AST_FLAG_CONCAT = 8,
TOY_AST_FLAG_ASSIGN = 10,
TOY_AST_FLAG_ADD_ASSIGN = 11,
TOY_AST_FLAG_SUBTRACT_ASSIGN = 12,
TOY_AST_FLAG_MULTIPLY_ASSIGN = 13,
TOY_AST_FLAG_DIVIDE_ASSIGN = 14,
TOY_AST_FLAG_MODULO_ASSIGN = 15,
TOY_AST_FLAG_COMPARE_EQUAL = 20,
TOY_AST_FLAG_COMPARE_NOT = 21,
TOY_AST_FLAG_COMPARE_LESS = 22,
TOY_AST_FLAG_COMPARE_LESS_EQUAL = 23,
TOY_AST_FLAG_COMPARE_GREATER = 24,
TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25,
TOY_AST_FLAG_COMPOUND_ARRAY = 30,
TOY_AST_FLAG_COMPOUND_TABLE = 31,
TOY_AST_FLAG_COLLECTION = 32,
TOY_AST_FLAG_PAIR = 33,
TOY_AST_FLAG_INDEX = 34,
//unary flags
TOY_AST_FLAG_NEGATE = 40,
TOY_AST_FLAG_PREFIX_INCREMENT = 41,
TOY_AST_FLAG_PREFIX_DECREMENT = 42,
TOY_AST_FLAG_POSTFIX_INCREMENT = 43,
TOY_AST_FLAG_POSTFIX_DECREMENT = 44,
TOY_AST_FLAG_INVOKATION = 45,
TOY_AST_FLAG_ATTRIBUTE = 46,
// TOY_AST_FLAG_TERNARY,
} Toy_AstFlag;
//the root AST type
typedef union Toy_Ast Toy_Ast;
typedef struct Toy_AstBlock {
Toy_AstType type;
bool innerScope;
Toy_Ast* child; //begin encoding the line
Toy_Ast* next; //'next' is either an AstBlock or null
Toy_Ast* tail; //'tail' - either points to the tail of the current list, or null; only used as an optimisation in toy_ast.c
} Toy_AstBlock;
typedef struct Toy_AstValue {
Toy_AstType type;
Toy_Value value;
} Toy_AstValue;
typedef struct Toy_AstUnary {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* child;
} Toy_AstUnary;
typedef struct Toy_AstBinary {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstBinary;
typedef struct Toy_AstBinaryShortCircuit {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstBinaryShortCircuit;
typedef struct Toy_AstCompare {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstCompare;
typedef struct Toy_AstGroup {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstGroup;
typedef struct Toy_AstCompound {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* child;
} Toy_AstCompound;
typedef struct Toy_AstAggregate {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstAggregate;
typedef struct Toy_AstAssert {
Toy_AstType type;
Toy_Ast* child;
Toy_Ast* message;
} Toy_AstAssert;
typedef struct Toy_AstIfThenElse {
Toy_AstType type;
Toy_Ast* condBranch;
Toy_Ast* thenBranch;
Toy_Ast* elseBranch;
} Toy_AstIfThenElse;
typedef struct Toy_AstWhileThen {
Toy_AstType type;
Toy_Ast* condBranch;
Toy_Ast* thenBranch;
} Toy_AstWhileThen;
typedef struct Toy_AstBreak {
Toy_AstType type;
} Toy_AstBreak;
typedef struct Toy_AstContinue {
Toy_AstType type;
} Toy_AstContinue;
typedef struct Toy_AstReturn {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstReturn;
typedef struct Toy_AstPrint {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstPrint;
typedef struct Toy_AstVarDeclare {
Toy_AstType type;
Toy_String* name;
Toy_Ast* expr;
Toy_ValueType valueType;
bool constant;
} Toy_AstVarDeclare;
typedef struct Toy_AstVarAssign {
Toy_AstType type;
Toy_AstFlag flag;
Toy_Ast* target;
Toy_Ast* expr;
} Toy_AstVarAssign;
typedef struct Toy_AstVarAccess {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstVarAccess;
typedef struct Toy_AstFnDeclare {
Toy_AstType type;
Toy_String* name;
Toy_Ast* params;
Toy_Ast* body;
} Toy_AstFnDeclare;
typedef struct Toy_AstFnInvoke {
Toy_AstType type;
Toy_Ast* function;
Toy_Ast* args;
} Toy_AstFnInvoke;
typedef struct Toy_AstAttribute {
Toy_AstType type;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstAttribute;
typedef struct Toy_AstStackPop {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstStackPop;
typedef struct Toy_AstPass {
Toy_AstType type;
} Toy_AstPass;
typedef struct Toy_AstError {
Toy_AstType type;
} Toy_AstError;
typedef struct Toy_AstEnd {
Toy_AstType type;
} Toy_AstEnd;
union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstType type;
Toy_AstBlock block;
Toy_AstValue value;
Toy_AstUnary unary;
Toy_AstBinary binary;
Toy_AstBinaryShortCircuit binaryShortCircuit;
Toy_AstCompare compare;
Toy_AstGroup group;
Toy_AstCompound compound;
Toy_AstAggregate aggregate;
Toy_AstAssert assert;
Toy_AstIfThenElse ifThenElse;
Toy_AstWhileThen whileThen;
Toy_AstBreak breakPoint;
Toy_AstContinue continuePoint;
Toy_AstReturn fnReturn;
Toy_AstPrint print;
Toy_AstVarDeclare varDeclare;
Toy_AstVarAssign varAssign;
Toy_AstVarAccess varAccess;
Toy_AstFnDeclare fnDeclare;
Toy_AstFnInvoke fnInvoke;
Toy_AstAttribute attribute;
Toy_AstStackPop stackPop;
Toy_AstPass pass;
Toy_AstError error;
Toy_AstEnd end;
};
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child);
void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value);
void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag);
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag);
void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr);
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr);
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body);
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params);
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
const char* Toy_private_getAstTypeAsCString(Toy_AstType type);
-385
View File
@@ -1,385 +0,0 @@
#include "toy_ast_node.h"
#include "toy_memory.h"
#include <stdio.h>
#include <stdlib.h>
static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
//don't free a NULL node
if (node == NULL) {
return;
}
switch(node->type) {
case TOY_AST_NODE_ERROR:
//NO-OP
break;
case TOY_AST_NODE_LITERAL:
Toy_freeLiteral(node->atomic.literal);
break;
case TOY_AST_NODE_UNARY:
Toy_freeASTNode(node->unary.child);
break;
case TOY_AST_NODE_BINARY:
Toy_freeASTNode(node->binary.left);
Toy_freeASTNode(node->binary.right);
break;
case TOY_AST_NODE_TERNARY:
Toy_freeASTNode(node->ternary.condition);
Toy_freeASTNode(node->ternary.thenPath);
Toy_freeASTNode(node->ternary.elsePath);
break;
case TOY_AST_NODE_GROUPING:
Toy_freeASTNode(node->grouping.child);
break;
case TOY_AST_NODE_BLOCK:
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
@@ -1,269 +0,0 @@
#pragma once
#include "toy_common.h"
#include "toy_literal.h"
#include "toy_opcodes.h"
#include "toy_token_types.h"
//nodes are the intermediaries between parsers and compilers
typedef union Toy_private_node Toy_ASTNode;
typedef enum Toy_ASTNodeType {
TOY_AST_NODE_ERROR,
TOY_AST_NODE_LITERAL, //a simple value
TOY_AST_NODE_UNARY, //one child + opcode
TOY_AST_NODE_BINARY, //two children, left and right + opcode
TOY_AST_NODE_TERNARY, //three children, condition, then path & else path
TOY_AST_NODE_GROUPING, //one child
TOY_AST_NODE_BLOCK, //contains a sub-node array
TOY_AST_NODE_COMPOUND, //contains a sub-node array
TOY_AST_NODE_PAIR, //contains a left and right
TOY_AST_NODE_INDEX, //index a variable
TOY_AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
TOY_AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
TOY_AST_NODE_FN_COLLECTION, //parts of a function
TOY_AST_NODE_FN_CALL, //call a function
TOY_AST_NODE_FN_RETURN, //for control flow
TOY_AST_NODE_IF, //for control flow
TOY_AST_NODE_WHILE, //for control flow
TOY_AST_NODE_FOR, //for control flow
TOY_AST_NODE_BREAK, //for control flow
TOY_AST_NODE_CONTINUE, //for control flow
TOY_AST_NODE_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);
+267
View File
@@ -0,0 +1,267 @@
#include "toy_attributes.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//if set, used for delegating to user-defined code
static Toy_OpaqueAttributeHandler opaqueAttributeCallback = NULL;
//NOTE: there is no need to call 'Toy_freeValue' on the arguments, as the VM assumes you don't
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_STRING(compound)->info.length);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "asUpper", 7) == 0) {
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
for (int i = 0; buffer[i] != '\0'; i++) {
buffer[i] = toupper(buffer[i]);
}
Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer));
free(buffer);
return TOY_VALUE_FROM_STRING(str);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "asLower", 7) == 0) {
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
for (int i = 0; buffer[i] != '\0'; i++) {
buffer[i] = tolower(buffer[i]);
}
Toy_String* str = Toy_createStringLength(&vm->memoryBucket, buffer, strlen(buffer));
free(buffer);
return TOY_VALUE_FROM_STRING(str);
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
}
static void attr_arrayPushBack(Toy_VM* vm) {
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value element = Toy_popStack(&vm->stack);
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
//BUGFIX: check the capacity limit
if (array->count == array->capacity) {
//correct the source value's pointer
array = Toy_resizeArray(array, array->capacity * TOY_ARRAY_EXPANSION_RATE);
if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_ARRAY) {
compound.as.reference->as.array = array;
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown error after expanding array size at %s %d", __FILE__, __LINE__);
Toy_error(buffer);
}
}
array->data[array->count] = element;
array->count++;
}
static void attr_arrayPopBack(Toy_VM* vm) {
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
//empty returns nothing
if (array->count == 0) {
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
return;
}
Toy_Value element = array->data[array->count-1];
array->count--;
Toy_pushStack(&vm->stack, element);
}
static void attr_arrayForEach(Toy_VM* vm) {
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value callback = Toy_popStack(&vm->stack);
if (TOY_VALUE_IS_FUNCTION(callback) != true) {
char buffer[256];
snprintf(buffer, 256, "Expected function, found '%s'", Toy_getValueTypeAsCString(callback.type));
Toy_error(buffer);
return;
}
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
Toy_Function* fn = TOY_VALUE_AS_FUNCTION(callback);
//this emulates 'processInvoke' a bit, but not entirely
Toy_VM subVM;
Toy_inheritVM(vm, &subVM);
switch(fn->type) {
case TOY_FUNCTION_CUSTOM: {
//push and run for each element
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
//bind to the subVM (more expensive than I'd like)
Toy_bindVM(&subVM, fn->bytecode.code, fn->bytecode.parentScope);
//get parameter name as a string
unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0];
Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]);
const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr;
Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr));
Toy_declareScope(subVM.scope, Toy_copyString(name), paramType, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]), true);
Toy_freeString(name);
Toy_runVM(&subVM); //TODO: could use a 'map'-style method by storing the results
Toy_resetVM(&subVM, false, true);
}
}
break;
case TOY_FUNCTION_NATIVE: {
//this uses a subVM for the native function, which is a slight difference than 'processInoke'
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
Toy_pushStack(&subVM.stack, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]));
fn->native.callback(&subVM); //NOTE: try not to leave anything on the stack afterwards
}
}
break;
default:
Toy_error("Can't call an unknown function type in 'forEach'");
break;
}
//cleanup
Toy_freeVM(&subVM);
}
static void attr_arraySort(Toy_VM* vm) {
(void)vm;
//URGENT: attr_arraySort
}
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "pushBack", 8) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPushBack);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "popBack", 7) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "forEach", 7) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayForEach);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "sort", 4) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
}
static void attr_tableInsert(Toy_VM* vm) {
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value value = Toy_popStack(&vm->stack); //NOTE: the args are still backwards, except compound
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
Toy_insertTable(&table, key, value);
//BUGFIX: check the capacity limit (Toy_insertTable automatically alters the pointer value)
if (TOY_VALUE_AS_TABLE(compound) != table) {
//correct the source value's pointer
if (TOY_VALUE_IS_REFERENCE(compound) && compound.as.reference->type == TOY_VALUE_TABLE) {
compound.as.reference->as.table = table;
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown error after expanding table size at %s %d", __FILE__, __LINE__);
Toy_error(buffer);
}
}
}
static void attr_tableHasKey(Toy_VM* vm) {
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(&table, key);
Toy_Value result = TOY_VALUE_FROM_BOOLEAN(entry != NULL);
Toy_pushStack(&vm->stack, result);
}
static void attr_tableRemove(Toy_VM* vm) {
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value key = Toy_popStack(&vm->stack);
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
Toy_removeTable(&table, key);
}
static void attr_tableForEach(Toy_VM* vm) {
(void)vm;
//URGENT: attr_tableForEach
}
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "insert", 6) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableInsert);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "hasKey", 6) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableHasKey);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "remove", 6) == 0) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableRemove);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "forEach", 7) == 0) { //URGENT: compare the contents AND length of these strings
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableForEach);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
}
Toy_Value Toy_private_handleOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (opaqueAttributeCallback == NULL) {
char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s' (did you set the opaque callbacks?)", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
Toy_error(buffer);
return TOY_VALUE_FROM_NULL();
}
return opaqueAttributeCallback(vm, compound, attribute);
}
void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb) {
opaqueAttributeCallback = cb;
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "toy_common.h"
#include "toy_value.h"
#include "toy_vm.h"
// [x] string.length
// [x] string.asUpper
// [x] string.asLower
// [x] array.length
// [x] array.pushBack(x)
// [x] array.popBack()
// [x] array.forEach(fn) // fn(x) -> void
// [ ] array.sort(fn) // fn(a,b) -> int
// [x] table.length
// [x] table.insert(x, y)
// [x] table.hasKey(x)
// [x] table.remove(x)
// [ ] table.forEach(fn) // fn(x,y) -> void
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
Toy_Value Toy_private_handleOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
//plug-and-play attributes for custom objects
typedef Toy_Value (*Toy_OpaqueAttributeHandler)(Toy_VM* vm, Toy_Value compound, Toy_Value attribute);
TOY_API void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb);
+123
View File
@@ -0,0 +1,123 @@
#include "toy_bucket.h"
#include "toy_console_colors.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//buckets of fun
Toy_Bucket* Toy_allocateBucket(unsigned int capacity) {
assert(capacity != 0 && "Cannot allocate a 'Toy_Bucket' with zero capacity");
Toy_Bucket* bucket = malloc(sizeof(Toy_Bucket) + capacity);
if (bucket == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Bucket' of %d capacity\n" TOY_CC_RESET, (int)capacity);
exit(1);
}
memset(bucket, 0, sizeof(Toy_Bucket) + capacity); //zero the memory, to avoid broken header metadata
//initialize the bucket
bucket->next = NULL;
bucket->capacity = capacity;
bucket->count = 0;
return bucket;
}
unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount) {
//the endpoint must be aligned to the word size, otherwise you'll get a bus error from moving pointers
amount = (amount + 3) & ~3; //NOTE: this also leaves the lowest two bits as zero
assert((*bucketHandle) != NULL && "Expected a 'Toy_Bucket', received NULL");
assert((*bucketHandle)->capacity >= (amount + 4) && "ERROR: Failed to partition a 'Toy_Bucket', requested amount is too high");
//if you're out of space in this bucket, allocate another one
if ((*bucketHandle)->capacity < (*bucketHandle)->count + amount + 4) { //+4 for the metadata header
Toy_Bucket* tmp = Toy_allocateBucket((*bucketHandle)->capacity);
tmp->next = (*bucketHandle); //it's buckets all the way down
(*bucketHandle) = tmp;
}
//use a 4-byte metadata header to hold the size of this partition, for GC
*((unsigned int*)((*bucketHandle)->data + (*bucketHandle)->count)) = amount;
//track the new metadata, and return the requested memory space
(*bucketHandle)->count += amount + 4;
return ((*bucketHandle)->data + (*bucketHandle)->count - amount); //metadata is before the pointer's address
}
void Toy_releaseBucketPartition(unsigned char* ptr) {
*((int*)(ptr-4)) |= 1; //flips the low-bit within the header
//no checks here, for technical reasons
}
void Toy_freeBucket(Toy_Bucket** bucketHandle) {
Toy_Bucket* iter = (*bucketHandle);
while (iter != NULL) {
//run down the chain
Toy_Bucket* last = iter;
iter = iter->next;
//clear the previous bucket from memory
free(last);
}
//for safety
(*bucketHandle) = NULL;
}
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
//clear whatever this handle is pointing to
if ((*bucketHandle) == NULL) {
return;
}
Toy_Bucket* link = *bucketHandle;
while (link) {
//find non-free partitions
unsigned char* ptr = link->data;
bool gc = true;
while (ptr - link->data < link->count) { //for each partition
if ( (*((int*)ptr) & 1) == 0) { //is this partition still in use?
gc = false;
break;
}
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
}
//free this link, if its been entirely released
if (gc) {
//if link is the head
if (link == (*bucketHandle)) {
//if there's nowhere to go, don't delete the whole bucket
if ((*bucketHandle)->next == NULL) {
return;
}
else {
(*bucketHandle) = (*bucketHandle)->next;
free(link);
link = (*bucketHandle);
}
}
else {
//find the prev and free this link before continuing
Toy_Bucket* it = (*bucketHandle);
while (it->next != link) {
it = it->next;
}
it->next = link->next;
free(link);
link = it->next;
}
}
else {
link = link->next;
}
}
}
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "toy_common.h"
//NOTE: this is an 'arena allocator', and has restrictions on it's usage:
// - It can only expand until it is freed
// - It cannot be copied or moved around in memory
// - It cannot allocate more memory than it has 'capacity'
// If each of these rules are followed, this is actually more efficient than other options
//a custom allocator
typedef struct Toy_Bucket { //32 | 64 BITNESS
struct Toy_Bucket* next; //4 | 8
unsigned int capacity; //4 | 4
unsigned int count; //4 | 4
unsigned char data[]; //- | -
} Toy_Bucket; //12 | 16
TOY_API Toy_Bucket* Toy_allocateBucket(unsigned int capacity);
TOY_API unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount);
TOY_API void Toy_releaseBucketPartition(unsigned char* ptr);
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
//standard capacity sizes
#ifndef TOY_BUCKET_1KB
#define TOY_BUCKET_1KB (1 << 10)
#endif
#ifndef TOY_BUCKET_2KB
#define TOY_BUCKET_2KB (1 << 11)
#endif
#ifndef TOY_BUCKET_4KB
#define TOY_BUCKET_4KB (1 << 12)
#endif
#ifndef TOY_BUCKET_8KB
#define TOY_BUCKET_8KB (1 << 13)
#endif
#ifndef TOY_BUCKET_16KB
#define TOY_BUCKET_16KB (1 << 14)
#endif
#ifndef TOY_BUCKET_32KB
#define TOY_BUCKET_32KB (1 << 15)
#endif
#ifndef TOY_BUCKET_64KB
#define TOY_BUCKET_64KB (1 << 16)
#endif
//CPU L1 caches tend to be 64kb, but that's far from guaranteed
#ifndef TOY_BUCKET_IDEAL
#define TOY_BUCKET_IDEAL (TOY_BUCKET_64KB - sizeof(Toy_Bucket))
#endif
-1474
View File
File diff suppressed because it is too large Load Diff
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "toy_interpreter.h"
//the _index function is a historical oddity - it's used whenever a compound is indexed
int Toy_private_index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
//globally available native functions
int Toy_private_set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
int Toy_private_clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
+4 -121
View File
@@ -1,125 +1,8 @@
#include "toy_common.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
//defined separately, as compilation can take several seconds, invalidating the comparisons of the given macros
static const char* build = __DATE__ " " __TIME__ ", incomplete Toy v2.x";
//test variable sizes based on platform
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
STATIC_ASSERT(sizeof(char) == 1);
STATIC_ASSERT(sizeof(short) == 2);
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(float) == 4);
STATIC_ASSERT(sizeof(unsigned char) == 1);
STATIC_ASSERT(sizeof(unsigned short) == 2);
STATIC_ASSERT(sizeof(unsigned int) == 4);
#ifndef TOY_EXPORT
//declare the singleton
Toy_CommandLine Toy_commandLine;
void Toy_initCommandLine(int argc, const char* argv[]) {
//default values
Toy_commandLine.error = false;
Toy_commandLine.help = false;
Toy_commandLine.version = false;
Toy_commandLine.binaryfile = NULL;
Toy_commandLine.sourcefile = NULL;
Toy_commandLine.compilefile = NULL;
Toy_commandLine.outfile = "out.tb";
Toy_commandLine.source = NULL;
Toy_commandLine.verbose = false;
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
Toy_commandLine.error = true; //error state by default, set to false by successful flags
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
Toy_commandLine.help = true;
Toy_commandLine.error = false;
continue;
}
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
Toy_commandLine.version = true;
Toy_commandLine.error = false;
continue;
}
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
Toy_commandLine.verbose = true;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
Toy_commandLine.sourcefile = (char*)argv[i + 1];
i++;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
Toy_commandLine.source = (char*)argv[i + 1];
i++;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
Toy_commandLine.compilefile = (char*)argv[i + 1];
i++;
Toy_commandLine.error = false;
continue;
}
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
Toy_commandLine.outfile = (char*)argv[i + 1];
i++;
Toy_commandLine.error = false;
continue;
}
//option without a flag + ending in .tb = binary input
if (i < argc) {
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
Toy_commandLine.binaryfile = (char*)argv[i];
Toy_commandLine.error = false;
continue;
}
}
//don't keep reading in an error state
return;
}
const char* Toy_private_versionBuild(void) {
return build;
}
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 Toy_helpCommandLine(int argc, const char* argv[]) {
Toy_usageCommandLine(argc, argv);
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
printf("-h\t| --help\t\tShow this help then exit.\n\n");
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
}
void Toy_copyrightCommandLine(int argc, const char* argv[]) {
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
printf("Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios\n\n");
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
}
#endif
+64 -35
View File
@@ -1,45 +1,74 @@
#pragma once
//for specified type sizes
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stddef.h>
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 8
#define TOY_VERSION_PATCH 2
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
//platform-specific specifications
//TOY_API is platform-dependant, and marks publicly usable API functions
#if defined(__linux__)
#define TOY_API extern
#elif defined(_WIN32) || defined(WIN32)
#define TOY_API
#define TOY_API extern
#elif defined(_WIN32) || defined(_WIN64)
#if defined(TOY_EXPORT)
#define TOY_API __declspec(dllexport)
#elif defined(TOY_IMPORT)
#define TOY_API __declspec(dllimport)
#else
#define TOY_API extern
#endif
#elif defined(__APPLE__)
#define TOY_API extern
#else
#define TOY_API
//generic solution
#define TOY_API extern
#endif
#ifndef TOY_EXPORT
//for processing the command line arguments
typedef struct {
bool error;
bool help;
bool version;
char* binaryfile;
char* sourcefile;
char* compilefile;
char* outfile; //defaults to out.tb
char* source;
bool verbose;
} Toy_CommandLine;
extern Toy_CommandLine Toy_commandLine;
void Toy_initCommandLine(int argc, const char* argv[]);
void Toy_usageCommandLine(int argc, const char* argv[]);
void Toy_helpCommandLine(int argc, const char* argv[]);
void Toy_copyrightCommandLine(int argc, const char* argv[]);
//TOY_BITNESS is used to encourage memory-cache friendliness
#if defined(__linux__)
#if defined(__LP64__)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#elif defined(__NetBSD__)
#if defined(__LP64__)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#elif defined(_WIN32) || defined(_WIN64)
#if defined(_WIN64)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#elif defined(__APPLE__)
#if defined(__LP64__)
#define TOY_BITNESS 64
#else
#define TOY_BITNESS 32
#endif
#else
//generic solution
#define TOY_BITNESS -1
#endif
//version specifiers, embedded as the header
#define TOY_VERSION_MAJOR 2
#define TOY_VERSION_MINOR 1
#define TOY_VERSION_PATCH 0
//defined as a function, for technical reasons
#define TOY_VERSION_BUILD Toy_private_versionBuild()
const char* Toy_private_versionBuild(void);
/*
Version validation rules:
* Under no circumstance, should you ever run code whose major version is different from the interpreters major version
* Under no circumstance, should you ever run code whose minor version is above the interpreters minor version
* You may, at your own risk, attempt to run code whose patch version is different from the interpreters patch version
* You may, at your own risk, attempt to run code whose build version is different from the interpreters build version
*/
+1378 -1240
View File
File diff suppressed because it is too large Load Diff
+54 -16
View File
@@ -1,22 +1,60 @@
#pragma once
#include "toy_common.h"
#include "toy_opcodes.h"
#include "toy_ast_node.h"
#include "toy_literal_array.h"
#include "toy_ast.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;
//the 'escapes' are lists of data used for processing the 'break' and 'continue' keywords
typedef struct Toy_private_EscapeEntry_t {
unsigned int addr; //the address to write *to*
unsigned int depth; //the current depth
} Toy_private_EscapeEntry_t;
typedef struct Toy_private_EscapeArray {
unsigned int capacity;
unsigned int count;
Toy_private_EscapeEntry_t data[];
} Toy_private_EscapeArray;
//not needed at runtime, so they can be bigger
#ifndef TOY_ESCAPE_INITIAL_CAPACITY
#define TOY_ESCAPE_INITIAL_CAPACITY 32
#endif
#ifndef TOY_ESCAPE_EXPANSION_RATE
#define TOY_ESCAPE_EXPANSION_RATE 4
#endif
Toy_private_EscapeArray* Toy_private_resizeEscapeArray(Toy_private_EscapeArray* ptr, unsigned int capacity);
//structure for holding the bytecode during compilation
typedef struct Toy_Bytecode {
unsigned char* code; //the instruction set
unsigned int codeCapacity;
unsigned int codeCount;
unsigned char* jumps; //each 'jump' is the starting address of an element within 'data'
unsigned int jumpsCapacity;
unsigned int jumpsCount;
unsigned char* param; //each 'param' is the starting address of a name string within 'data'
unsigned int paramCapacity;
unsigned int paramCount;
unsigned char* data; //a block of read-only data
unsigned int dataCapacity;
unsigned int dataCount;
unsigned char* subs; //subroutines etc, built recursively
unsigned int subsCapacity;
unsigned int subsCount;
//tools for handling the build process
unsigned int currentScopeDepth;
Toy_private_EscapeArray* breakEscapes;
Toy_private_EscapeArray* continueEscapes;
//compilation errors
bool panic;
} Toy_Compiler;
} Toy_Bytecode;
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);
TOY_API unsigned char* Toy_compileToBytecode(Toy_Ast* ast);
+74 -22
View File
@@ -1,30 +1,82 @@
#pragma once
//NOTE: you need both font AND background for these to work
/* toy_console_colors.h - console utility
This file provides a number of macros that can set the color of text in a console
window. These are used for convenience only. They are supposed to be dropped into
a printf()'s first argument, like so:
printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET);
reference: https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences
*/
//platform/compiler-specific instructions
#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__)
//fonts color
#define TOY_CC_FONT_BLACK "\033[30;"
#define TOY_CC_FONT_RED "\033[31;"
#define TOY_CC_FONT_GREEN "\033[32;"
#define TOY_CC_FONT_YELLOW "\033[33;"
#define TOY_CC_FONT_BLUE "\033[34;"
#define TOY_CC_FONT_PURPLE "\033[35;"
#define TOY_CC_FONT_DGREEN "\033[6;"
#define TOY_CC_FONT_WHITE "\033[7;"
#define TOY_CC_FONT_CYAN "\x1b[36m"
#define TOY_CC_FONT_BLACK "30"
#define TOY_CC_FONT_RED "31"
#define TOY_CC_FONT_GREEN "32"
#define TOY_CC_FONT_YELLOW "33"
#define TOY_CC_FONT_BLUE "34"
#define TOY_CC_FONT_MAGENTA "35"
#define TOY_CC_FONT_CYAN "36"
#define TOY_CC_FONT_WHITE "37"
#define TOY_CC_FONT_DEFAULT "39"
//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"
#define TOY_CC_BACK_BLACK "40"
#define TOY_CC_BACK_RED "41"
#define TOY_CC_BACK_GREEN "42"
#define TOY_CC_BACK_YELLOW "43"
#define TOY_CC_BACK_BLUE "44"
#define TOY_CC_BACK_MAGENTA "45"
#define TOY_CC_BACK_CYAN "46"
#define TOY_CC_BACK_WHITE "47"
#define TOY_CC_BACK_DEFAULT "49"
//useful macros
#define TOY_CC_DEBUG "\033[" TOY_CC_FONT_BLUE ";" TOY_CC_BACK_DEFAULT "m"
#define TOY_CC_NOTICE "\033[" TOY_CC_FONT_GREEN ";" TOY_CC_BACK_DEFAULT "m"
#define TOY_CC_WARN "\033[" TOY_CC_FONT_YELLOW ";" TOY_CC_BACK_DEFAULT "m"
#define TOY_CC_ERROR "\033[" TOY_CC_FONT_RED ";" TOY_CC_BACK_DEFAULT "m"
#define TOY_CC_ASSERT "\033[" TOY_CC_FONT_BLACK ";" TOY_CC_BACK_MAGENTA "m"
#define TOY_CC_RESET "\033[" TOY_CC_FONT_DEFAULT ";" TOY_CC_BACK_DEFAULT "m"
//for unsupported platforms, these become no-ops
#else
//fonts color
#define TOY_CC_FONT_BLACK
#define TOY_CC_FONT_RED
#define TOY_CC_FONT_GREEN
#define TOY_CC_FONT_YELLOW
#define TOY_CC_FONT_BLUE
#define TOY_CC_FONT_MAGENTA
#define TOY_CC_FONT_CYAN
#define TOY_CC_FONT_WHITE
#define TOY_CC_FONT_DEFAULT
//background color
#define TOY_CC_BACK_BLACK
#define TOY_CC_BACK_RED
#define TOY_CC_BACK_GREEN
#define TOY_CC_BACK_YELLOW
#define TOY_CC_BACK_BLUE
#define TOY_CC_BACK_MAGENTA
#define TOY_CC_BACK_CYAN
#define TOY_CC_BACK_WHITE
#define TOY_CC_BACK_DEFAULT
//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"
#define TOY_CC_DEBUG
#define TOY_CC_NOTICE
#define TOY_CC_WARN
#define TOY_CC_ERROR
#define TOY_CC_ASSERT
#define TOY_CC_RESET
#endif
+49
View File
@@ -0,0 +1,49 @@
#include "toy_function.h"
Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope) {
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
fn->type = TOY_FUNCTION_CUSTOM;
fn->bytecode.code = bytecode;
fn->bytecode.parentScope = parentScope;
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
return fn;
}
Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback) {
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
fn->type = TOY_FUNCTION_NATIVE;
fn->native.callback = callback;
return fn;
}
Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* original) {
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
if (original->type == TOY_FUNCTION_CUSTOM) {
fn->type = original->type;
fn->bytecode.code = original->bytecode.code;
fn->bytecode.parentScope = original->bytecode.parentScope;
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
}
else if (fn->type == TOY_FUNCTION_NATIVE) {
fn->type = original->type;
fn->native.callback = original->native.callback;
}
return fn;
}
TOY_API void Toy_freeFunction(Toy_Function* fn) {
if (fn->type == TOY_FUNCTION_CUSTOM) {
Toy_private_decrementScopeRefCount(fn->bytecode.parentScope);
}
else if (fn->type == TOY_FUNCTION_NATIVE) {
fn->native.callback = NULL;
}
Toy_releaseBucketPartition((void*)fn);
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "toy_common.h"
#include "toy_bucket.h"
#include "toy_scope.h"
#include "toy_vm.h"
//forward declare
struct Toy_VM;
typedef void (*Toy_nativeCallback)(struct Toy_VM*);
typedef enum Toy_FunctionType {
TOY_FUNCTION_CUSTOM,
TOY_FUNCTION_NATIVE,
} Toy_FunctionType;
typedef struct Toy_FunctionBytecode {
Toy_FunctionType type;
unsigned char* code;
Toy_Scope* parentScope;
} Toy_FunctionBytecode;
typedef struct Toy_FunctionNative {
Toy_FunctionType type;
Toy_nativeCallback callback;
} Toy_FunctionNative;
typedef union Toy_Function_t {
Toy_FunctionType type;
Toy_FunctionBytecode bytecode;
Toy_FunctionNative native;
} Toy_Function;
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope);
TOY_API Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback);
TOY_API Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* fn);
TOY_API void Toy_freeFunction(Toy_Function* fn);
File diff suppressed because it is too large Load Diff
-53
View File
@@ -1,53 +0,0 @@
#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
@@ -1,77 +0,0 @@
#include "toy_keyword_types.h"
#include "toy_common.h"
#include <string.h>
Toy_KeywordType Toy_keywordTypes[] = {
//type keywords
{TOY_TOKEN_NULL, "null"},
{TOY_TOKEN_BOOLEAN, "bool"},
{TOY_TOKEN_INTEGER, "int"},
{TOY_TOKEN_FLOAT, "float"},
{TOY_TOKEN_STRING, "string"},
{TOY_TOKEN_FUNCTION, "fn"},
{TOY_TOKEN_OPAQUE, "opaque"},
{TOY_TOKEN_ANY, "any"},
//other keywords
{TOY_TOKEN_AS, "as"},
{TOY_TOKEN_ASSERT, "assert"},
{TOY_TOKEN_BREAK, "break"},
{TOY_TOKEN_CLASS, "class"},
{TOY_TOKEN_CONST, "const"},
{TOY_TOKEN_CONTINUE, "continue"},
{TOY_TOKEN_DO, "do"},
{TOY_TOKEN_ELSE, "else"},
{TOY_TOKEN_EXPORT, "export"},
{TOY_TOKEN_FOR, "for"},
{TOY_TOKEN_FOREACH, "foreach"},
{TOY_TOKEN_IF, "if"},
{TOY_TOKEN_IMPORT, "import"},
{TOY_TOKEN_IN, "in"},
{TOY_TOKEN_OF, "of"},
{TOY_TOKEN_PRINT, "print"},
{TOY_TOKEN_RETURN, "return"},
{TOY_TOKEN_TYPE, "type"},
{TOY_TOKEN_ASTYPE, "astype"},
{TOY_TOKEN_TYPEOF, "typeof"},
{TOY_TOKEN_VAR, "var"},
{TOY_TOKEN_WHILE, "while"},
//literal values
{TOY_TOKEN_LITERAL_TRUE, "true"},
{TOY_TOKEN_LITERAL_FALSE, "false"},
//meta tokens
{TOY_TOKEN_PASS, NULL},
{TOY_TOKEN_ERROR, NULL},
{TOY_TOKEN_EOF, NULL},
};
char* Toy_findKeywordByType(Toy_TokenType type) {
if (type == TOY_TOKEN_EOF) {
return "EOF";
}
for(int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (Toy_keywordTypes[i].type == type) {
return Toy_keywordTypes[i].keyword;
}
}
return NULL;
}
Toy_TokenType Toy_findTypeByKeyword(const char* keyword) {
const int length = strlen(keyword);
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (!strncmp(keyword, Toy_keywordTypes[i].keyword, length)) {
return Toy_keywordTypes[i].type;
}
}
return TOY_TOKEN_EOF;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "toy_token_types.h"
typedef struct {
Toy_TokenType type;
char* keyword;
} Toy_KeywordType;
extern Toy_KeywordType Toy_keywordTypes[];
char* Toy_findKeywordByType(Toy_TokenType type);
Toy_TokenType Toy_findTypeByKeyword(const char* keyword);
+165 -122
View File
@@ -1,18 +1,94 @@
#include "toy_lexer.h"
#include "toy_console_colors.h"
#include "toy_keyword_types.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//keyword data
typedef struct {
const Toy_TokenType type;
const char* keyword;
} Toy_KeywordTypeTuple;
const Toy_KeywordTypeTuple keywordTuples[] = {
//null
{TOY_TOKEN_NULL, "null"},
//types
{TOY_TOKEN_TYPE_BOOLEAN, "Bool"},
{TOY_TOKEN_TYPE_INTEGER, "Int"},
{TOY_TOKEN_TYPE_FLOAT, "Float"},
{TOY_TOKEN_TYPE_STRING, "String"},
{TOY_TOKEN_TYPE_ARRAY, "Array"},
{TOY_TOKEN_TYPE_TABLE, "Table"},
{TOY_TOKEN_TYPE_FUNCTION, "Function"},
{TOY_TOKEN_TYPE_OPAQUE, "Opaque"},
{TOY_TOKEN_TYPE_ANY, "Any"},
//keywords and reserved words
{TOY_TOKEN_KEYWORD_AS, "as"},
{TOY_TOKEN_KEYWORD_ASSERT, "assert"},
{TOY_TOKEN_KEYWORD_BREAK, "break"},
{TOY_TOKEN_KEYWORD_CLASS, "class"},
{TOY_TOKEN_KEYWORD_CONST, "const"},
{TOY_TOKEN_KEYWORD_CONTINUE, "continue"},
{TOY_TOKEN_KEYWORD_DO, "do"},
{TOY_TOKEN_KEYWORD_ELSE, "else"},
{TOY_TOKEN_KEYWORD_EXPORT, "export"},
{TOY_TOKEN_KEYWORD_FOR, "for"},
{TOY_TOKEN_KEYWORD_FOREACH, "foreach"},
{TOY_TOKEN_KEYWORD_FUNCTION, "fn"},
{TOY_TOKEN_KEYWORD_IF, "if"},
{TOY_TOKEN_KEYWORD_IMPORT, "import"},
{TOY_TOKEN_KEYWORD_IN, "in"},
{TOY_TOKEN_KEYWORD_OF, "of"},
{TOY_TOKEN_KEYWORD_PASS, "pass"},
{TOY_TOKEN_KEYWORD_PRINT, "print"},
{TOY_TOKEN_KEYWORD_RETURN, "return"},
{TOY_TOKEN_KEYWORD_VAR, "var"},
{TOY_TOKEN_KEYWORD_WHILE, "while"},
{TOY_TOKEN_KEYWORD_YIELD, "yield"},
//literal values
{TOY_TOKEN_LITERAL_TRUE, "true"},
{TOY_TOKEN_LITERAL_FALSE, "false"},
{TOY_TOKEN_EOF, NULL},
};
const char* Toy_private_findKeywordByType(const Toy_TokenType type) {
if (type == TOY_TOKEN_EOF) {
return "EOF";
}
for(int i = 0; keywordTuples[i].keyword; i++) {
if (keywordTuples[i].type == type) {
return keywordTuples[i].keyword;
}
}
return NULL;
}
Toy_TokenType Toy_private_findTypeByKeyword(const char* keyword) {
const int length = strlen(keyword);
for (int i = 0; keywordTuples[i].keyword; i++) {
if (!strncmp(keyword, keywordTuples[i].keyword, length)) {
return keywordTuples[i].type;
}
}
return TOY_TOKEN_EOF;
}
//static generic utility functions
static void cleanLexer(Toy_Lexer* lexer) {
lexer->source = NULL;
lexer->start = 0;
lexer->current = 0;
lexer->line = 1;
lexer->commentsEnabled = true;
lexer->source = NULL;
}
static bool isAtEnd(Toy_Lexer* lexer) {
@@ -55,10 +131,6 @@ static void eatWhitespace(Toy_Lexer* lexer) {
//comments
case '/':
if (!lexer->commentsEnabled) {
return;
}
//eat the line
if (peekNext(lexer) == '/') {
while (!isAtEnd(lexer) && advance(lexer) != '\n');
@@ -110,16 +182,9 @@ 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
token.lexeme = msg;
return token;
}
@@ -129,48 +194,33 @@ static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
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
token.lexeme = &lexer->source[lexer->current - token.length];
return token;
}
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //assume we're reading an integer
//the character '_' can be inserted into numbers as a separator
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
type = TOY_TOKEN_LITERAL_FLOAT;
advance(lexer);
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //peekNext(lexer) == digit
type = TOY_TOKEN_LITERAL_FLOAT; //change the assumption to reading a float
advance(lexer); //eat the '.'
//'_' again
while(isDigit(lexer) || peek(lexer) == '_') advance(lexer);
}
//make the token
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
token.lexeme = &lexer->source[lexer->start];
return token;
}
@@ -192,7 +242,6 @@ 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;
}
@@ -211,24 +260,20 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
return makeErrorToken(lexer, "Unterminated string");
}
advance(lexer); //eat the terminator
//make the token
Toy_Token token;
token.type = TOY_TOKEN_LITERAL_STRING;
token.lexeme = &lexer->source[lexer->start + 1];
token.length = lexer->current - lexer->start - 2;
token.length = lexer->current - lexer->start - 2; //-1 to omit the quotes
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("str:");
Toy_printToken(&token);
}
#endif
token.lexeme = &lexer->source[lexer->start + 1]; //+1 to omit the first quote
return token;
}
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
static Toy_Token makeKeywordOrName(Toy_Lexer* lexer) {
advance(lexer); //first letter can only be alpha
while(isDigit(lexer) || isAlpha(lexer)) {
@@ -236,52 +281,43 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
}
//scan for a keyword
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
for (int i = 0; keywordTuples[i].keyword; i++) {
//WONTFIX: could squeeze miniscule performance gain from this, but ROI isn't worth it
if (strlen(keywordTuples[i].keyword) == (lexer->current - lexer->start) && !strncmp(keywordTuples[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
//make token (keyword)
Toy_Token token;
token.type = Toy_keywordTypes[i].type;
token.lexeme = &lexer->source[lexer->start];
token.type = keywordTuples[i].type;
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("kwd:");
Toy_printToken(&token);
}
#endif
token.lexeme = &lexer->source[lexer->start];
return token;
}
}
//return an identifier
//make token (variable name)
Toy_Token token;
token.type = TOY_TOKEN_IDENTIFIER;
token.lexeme = &lexer->source[lexer->start];
token.type = TOY_TOKEN_NAME;
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (Toy_commandLine.verbose) {
printf("idf:");
Toy_printToken(&token);
}
#endif
token.lexeme = &lexer->source[lexer->start];
return token;
}
//exposed functions
void Toy_initLexer(Toy_Lexer* lexer, const char* source) {
void Toy_bindLexer(Toy_Lexer* lexer, const char* source) {
cleanLexer(lexer);
lexer->source = source;
}
Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
if (lexer->source == NULL) {
return makeErrorToken(lexer, "Missing source code in lexer");
}
eatWhitespace(lexer);
lexer->start = lexer->current;
@@ -289,95 +325,102 @@ Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
if (isAlpha(lexer)) return makeKeywordOrName(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, TOY_TOKEN_OPERATOR_PAREN_LEFT);
case ')': return makeToken(lexer, TOY_TOKEN_OPERATOR_PAREN_RIGHT);
case '[': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACKET_LEFT);
case ']': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACKET_RIGHT);
case '{': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACE_LEFT);
case '}': return makeToken(lexer, TOY_TOKEN_OPERATOR_BRACE_RIGHT);
case '+': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOY_TOKEN_PLUS_PLUS: TOY_TOKEN_PLUS);
case '-': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_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_OPERATOR_ADD_ASSIGN : match(lexer, '+') ? TOY_TOKEN_OPERATOR_INCREMENT : TOY_TOKEN_OPERATOR_ADD);
case '-': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_SUBTRACT_ASSIGN : match(lexer, '-') ? TOY_TOKEN_OPERATOR_DECREMENT : TOY_TOKEN_OPERATOR_SUBTRACT);
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN : TOY_TOKEN_OPERATOR_MULTIPLY);
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN : TOY_TOKEN_OPERATOR_DIVIDE);
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_MODULO_ASSIGN : TOY_TOKEN_OPERATOR_MODULO);
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_NOT_EQUAL : TOY_TOKEN_NOT);
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_EQUAL : TOY_TOKEN_ASSIGN);
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_NOT : TOY_TOKEN_OPERATOR_NEGATE);
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_EQUAL : TOY_TOKEN_OPERATOR_ASSIGN);
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_LESS_EQUAL : TOY_TOKEN_LESS);
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_GREATER_EQUAL : TOY_TOKEN_GREATER);
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_LESS_EQUAL : TOY_TOKEN_OPERATOR_COMPARE_LESS);
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL : TOY_TOKEN_OPERATOR_COMPARE_GREATER);
case '&': //TOKEN_AND not used
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
case '&': //TOY_TOKEN_OPERATOR_AMPERSAND is unused
if (match(lexer, '&')) {
return makeToken(lexer, TOY_TOKEN_OPERATOR_AND);
} else {
return makeToken(lexer, TOY_TOKEN_AND);
return makeErrorToken(lexer, "Unexpected '&'");
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
case '|': //TOY_TOKEN_OPERATOR_PIPE is unused
if (match(lexer, '|')) {
return makeToken(lexer, TOY_TOKEN_OPERATOR_OR);
} else {
return makeErrorToken(lexer, "Unexpected '|'");
}
case '?': return makeToken(lexer, TOY_TOKEN_OPERATOR_QUESTION);
case ':': return makeToken(lexer, TOY_TOKEN_OPERATOR_COLON);
case ';': return makeToken(lexer, TOY_TOKEN_OPERATOR_SEMICOLON);
case ',': return makeToken(lexer, TOY_TOKEN_OPERATOR_COMMA);
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
case ';': return makeToken(lexer, TOY_TOKEN_SEMICOLON);
case ',': return makeToken(lexer, TOY_TOKEN_COMMA);
case '.':
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
advance(lexer);
advance(lexer);
return makeToken(lexer, TOY_TOKEN_REST);
if (match(lexer, '.')) {
if (match(lexer, '.')) {
return makeToken(lexer, TOY_TOKEN_OPERATOR_REST); //three dots
}
else {
return makeToken(lexer, TOY_TOKEN_OPERATOR_CONCAT); //two dots
}
}
else {
return makeToken(lexer, TOY_TOKEN_OPERATOR_DOT); //one dot
}
return makeToken(lexer, TOY_TOKEN_DOT);
case '"':
return makeString(lexer, c);
//TODO: possibly support interpolated strings
default: {
char buffer[128];
snprintf(buffer, 128, "Unexpected token: %c", c);
return makeErrorToken(lexer, buffer);
return makeErrorToken(lexer, "Unknown token value found in lexer");
}
}
}
static void trim(char** s, int* l) { //all this to remove a newline?
static void trim(char** s, unsigned int* l) { //util
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
}
//for debugging
void Toy_printToken(Toy_Token* token) {
void Toy_private_printToken(Toy_Token* token) {
//print errors
if (token->type == TOY_TOKEN_ERROR) {
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
printf(TOY_CC_ERROR "ERROR: \t%d\t%.*s\n" TOY_CC_RESET, (int)token->line, (int)token->length, token->lexeme);
return;
}
printf("\t%d\t%d\t", token->type, token->line);
//print the line number
printf("\t%d\t%d\t", token->type, (int)token->line);
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
printf("%.*s\t", token->length, token->lexeme);
//print based on type
if (token->type == TOY_TOKEN_NAME || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
printf("%.*s\t", (int)token->length, token->lexeme);
} else {
char* keyword = Toy_findKeywordByType(token->type);
const char* keyword = Toy_private_findKeywordByType(token->type);
if (keyword != NULL) {
printf("%s", keyword);
} else {
char* str = (char*)token->lexeme; //strip const-ness for trimming
int length = token->length;
unsigned int length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
printf("%.*s", (int)length, str);
}
}
printf("\n");
}
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled) {
lexer->commentsEnabled = enabled;
}
+11 -12
View File
@@ -3,27 +3,26 @@
#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
//lexers are bound to a string of code
typedef struct {
unsigned int start; //start of the current token
unsigned int current; //current position of the lexer
unsigned int line; //track this for error handling
const char* source;
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;
unsigned int length;
unsigned int line;
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);
TOY_API void Toy_bindLexer(Toy_Lexer* lexer, const char* source);
Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer);
//for debugging
void Toy_printToken(Toy_Token* token);
const char* Toy_private_findKeywordByType(const Toy_TokenType type);
Toy_TokenType Toy_private_findTypeByKeyword(const char* keyword);
void Toy_private_printToken(Toy_Token* token);
void Toy_private_setComments(Toy_Lexer* lexer, bool enabled);
-726
View File
@@ -1,726 +0,0 @@
#include "toy_literal.h"
#include "toy_memory.h"
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
#include "toy_scope.h"
#include "toy_console_colors.h"
#include <stdio.h>
//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
@@ -1,149 +0,0 @@
#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
@@ -1,98 +0,0 @@
#include "toy_literal_array.h"
#include "toy_memory.h"
#include <stdio.h>
#include <string.h>
//exposed functions
void Toy_initLiteralArray(Toy_LiteralArray* array) {
array->capacity = 0;
array->count = 0;
array->literals = NULL;
}
void Toy_freeLiteralArray(Toy_LiteralArray* array) {
//clean up memory
for(int i = 0; i < array->count; i++) {
Toy_freeLiteral(array->literals[i]);
}
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
@@ -1,20 +0,0 @@
#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
@@ -1,219 +0,0 @@
#include "toy_literal_dictionary.h"
#include "toy_memory.h"
#include "toy_console_colors.h"
#include <stdio.h>
//util functions
static void setEntryValues(Toy_private_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
@@ -1,29 +0,0 @@
#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
@@ -1,53 +0,0 @@
#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
@@ -1,18 +0,0 @@
#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);
+57 -74
View File
@@ -1,87 +1,70 @@
#pragma once
typedef enum Toy_Opcode {
TOY_OP_EOF,
typedef enum Toy_OpcodeType {
//This offsets the opcode values, so I can see TOY_OPCODE_READ in GDB clearly
TOY_OPCODE_UNUSED = 0,
//basic statements
TOY_OP_ASSERT,
TOY_OP_PRINT,
//variable instructions
TOY_OPCODE_READ,
TOY_OPCODE_DECLARE,
TOY_OPCODE_ASSIGN,
TOY_OPCODE_ASSIGN_COMPOUND, //assign to a compound's internals
TOY_OPCODE_ACCESS,
TOY_OPCODE_INVOKE, //for calling functions
TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds
TOY_OPCODE_DUPLICATE, //duplicate the top of the stack
TOY_OPCODE_ELIMINATE, //remove the top of the stack
//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 instructions
TOY_OPCODE_ADD,
TOY_OPCODE_SUBTRACT,
TOY_OPCODE_MULTIPLY,
TOY_OPCODE_DIVIDE,
TOY_OPCODE_MODULO,
//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,
//comparison instructions
TOY_OPCODE_COMPARE_EQUAL,
// TOY_OPCODE_COMPARE_NOT, //NOTE: optimized into a composite of TOY_OPCODE_COMPARE_EQUAL + TOY_OPCODE_NEGATE
TOY_OPCODE_COMPARE_LESS,
TOY_OPCODE_COMPARE_LESS_EQUAL,
TOY_OPCODE_COMPARE_GREATER,
TOY_OPCODE_COMPARE_GREATER_EQUAL,
//variable stuff
TOY_OP_SCOPE_BEGIN,
TOY_OP_SCOPE_END,
//logical instructions
TOY_OPCODE_AND,
TOY_OPCODE_OR,
TOY_OPCODE_TRUTHY,
TOY_OPCODE_NEGATE,
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)
//control instructions
TOY_OPCODE_RETURN,
TOY_OPCODE_JUMP, //JUMP, ADDR
TOY_OPCODE_ESCAPE, //JUMP, ADDR, UNWIND
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_OPCODE_SCOPE_PUSH,
TOY_OPCODE_SCOPE_POP,
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)
//various action instructions
TOY_OPCODE_ASSERT,
TOY_OPCODE_PRINT,
TOY_OPCODE_CONCAT,
TOY_OPCODE_INDEX,
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,
//meta instructions
TOY_OPCODE_PASS,
TOY_OPCODE_ERROR,
TOY_OPCODE_EOF = 255,
} Toy_OpcodeType;
TOY_OP_TYPE_CAST, //temporarily change a type of an atomic value
TOY_OP_TYPE_OF, //get the type of a variable
//specific opcode flags
typedef enum Toy_OpParamJumpType {
TOY_OP_PARAM_JUMP_ABSOLUTE = 0, //from the start of the routine's code section
TOY_OP_PARAM_JUMP_RELATIVE = 1,
} Toy_OpParamJumpType;
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;
typedef enum Toy_OpParamJumpConditional {
TOY_OP_PARAM_JUMP_ALWAYS = 0,
TOY_OP_PARAM_JUMP_IF_TRUE = 1,
TOY_OP_PARAM_JUMP_IF_FALSE = 2,
} Toy_OpParamJumpConditional;
+936 -1598
View File
File diff suppressed because it is too large Load Diff
+10 -9
View File
@@ -1,20 +1,21 @@
#pragma once
#include "toy_common.h"
#include "toy_lexer.h"
#include "toy_ast_node.h"
#include "toy_ast.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
typedef struct Toy_Parser {
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
//last two outputs
Toy_Token current;
Toy_Token previous;
bool error;
bool panic; //currently processing an error
} 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);
TOY_API void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer);
TOY_API Toy_Ast* Toy_scanParser(Toy_Bucket** bucketHandle, Toy_Parser* parser);
TOY_API void Toy_resetParser(Toy_Parser* parser);
+43
View File
@@ -0,0 +1,43 @@
#include "toy_print.h"
#include <stdio.h>
static Toy_callbackType printCallback = puts;
static Toy_callbackType errorCallback = puts;
static Toy_callbackType assertCallback = puts;
void Toy_print(const char* msg) {
printCallback(msg);
}
void Toy_error(const char* msg) {
errorCallback(msg);
}
void Toy_assertFailure(const char* msg) {
assertCallback(msg);
}
void Toy_setPrintCallback(Toy_callbackType cb) {
printCallback = cb;
}
void Toy_setErrorCallback(Toy_callbackType cb) {
errorCallback = cb;
}
void Toy_setAssertFailureCallback(Toy_callbackType cb) {
assertCallback = cb;
}
void Toy_resetPrintCallback(void) {
printCallback = puts;
}
void Toy_resetErrorCallback(void) {
errorCallback = puts;
}
void Toy_resetAssertFailureCallback(void) {
assertCallback = puts;
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "toy_common.h"
//handle callbacks for printing to the terminal, or elsewhere (signature matches 'puts')
typedef int (*Toy_callbackType)(const char*);
TOY_API void Toy_print(const char* msg); //print keyword
TOY_API void Toy_error(const char* msg); //runtime errors
TOY_API void Toy_assertFailure(const char* msg); //assert keyword failures
TOY_API void Toy_setPrintCallback(Toy_callbackType cb);
TOY_API void Toy_setErrorCallback(Toy_callbackType cb);
TOY_API void Toy_setAssertFailureCallback(Toy_callbackType cb);
TOY_API void Toy_resetPrintCallback(void);
TOY_API void Toy_resetErrorCallback(void);
TOY_API void Toy_resetAssertFailureCallback(void);
-95
View File
@@ -1,95 +0,0 @@
#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
@@ -1,27 +0,0 @@
#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);
+232 -304
View File
@@ -1,324 +1,252 @@
#include "toy_scope.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Toy_Scope* scope) {
scope->references--;
#include "toy_print.h"
//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
//utils
static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) {
//terminate
if (scope == NULL || scope->data == NULL) {
return NULL;
}
Toy_Scope* ret = scope->ancestor;
//probe for the correct location
unsigned int probe = hash % scope->capacity;
//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;
while (true) {
//found the entry
if (scope->data[probe].key != NULL && Toy_compareStrings(scope->data[probe].key, key) == 0) {
return &(scope->data[probe]);
}
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;
//if its an empty slot (didn't find it here)
if (scope->data[probe].key == NULL) {
return recursive ? lookupScopeEntryPtr(scope->next, key, hash, recursive) : NULL;
}
//adjust and continue
probe = (probe + 1) % scope->capacity;
}
}
static void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_ValueType type, bool constant) {
//make the entry
unsigned int probe = Toy_hashString(key) % scope->capacity;
Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = key, .value = value, .type = type, .constant = constant, .psl = 1 };
//probe
while (true) {
//if we're overriding an existing value
if (scope->data[probe].key != NULL && Toy_compareStrings(scope->data[probe].key, entry.key) == 0) {
scope->data[probe] = entry;
scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl;
return;
}
//if this spot is free, insert and return
if (TOY_VALUE_IS_NULL(scope->data[probe].value)) {
scope->data[probe] = entry;
scope->count++;
scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl;
return;
}
//if the new entry is "poorer", insert it and shift the old one
if (scope->data[probe].psl < entry.psl) {
Toy_ScopeEntry tmp = scope->data[probe];
scope->data[probe] = entry;
entry = tmp;
}
//adjust and continue
probe++;
probe &= scope->capacity - 1; //DOOM hack
entry.psl++;
}
}
static Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapacity) {
//allocate and zero a new Toy_ScopeEntry array in memory
Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry));
if (newEntries == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space for 'Toy_Scope' entries\n" TOY_CC_RESET);
exit(1);
}
//wipe the memory
memset(newEntries, 0, newCapacity * sizeof(Toy_ScopeEntry));
if (scope == NULL) { //for initial allocations
return newEntries;
}
//move the old data into the new block of memory
unsigned int oldCapacity = scope->capacity;
Toy_ScopeEntry* oldEntries = scope->data;
scope->capacity = newCapacity;
scope->data = newEntries;
scope->count = 0;
//for each existing entry in the old array, copy it into the new array
for (unsigned int i = 0; i < oldCapacity; i++) {
if (oldEntries[i].key != NULL && oldEntries[i].key->info.length > 0) {
probeAndInsert(scope, oldEntries[i].key, oldEntries[i].value, oldEntries[i].type, oldEntries[i].constant);
}
}
freeAncestorChain(scope);
return ret;
//clean up and return
free(oldEntries);
return newEntries;
}
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);
Toy_Value coerceValueTypesIfAble(Toy_ValueType type, Toy_Value value) {
//integer to float
if (type == TOY_VALUE_FLOAT && value.type == TOY_VALUE_INTEGER) {
value = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(value) );
}
return value;
}
//tick up all scope reference counts
scope->references = 0;
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
//exposed functions
Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
newScope->next = scope;
newScope->refCount = 0;
newScope->data = adjustScopeEntries(NULL, TOY_SCOPE_INITIAL_CAPACITY);
newScope->capacity = TOY_SCOPE_INITIAL_CAPACITY;
newScope->count = 0;
newScope->maxPsl = 0;
Toy_private_incrementScopeRefCount(newScope);
return newScope;
}
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
if (scope == NULL) {
return NULL;
}
//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);
Toy_private_decrementScopeRefCount(scope);
Toy_Scope* next = scope->next;
Toy_releaseBucketPartition((void*)scope);
return next;
}
void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant) {
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), false);
if (entryPtr != NULL) {
char buffer[key->info.length + 256];
sprintf(buffer, "Can't redefine a variable: %s", key->leaf.data);
Toy_error(buffer);
return;
}
//expand the table capacity if needed
if (scope->count >= scope->capacity * 0.8f) {
scope->data = adjustScopeEntries(scope, scope->capacity * TOY_SCOPE_EXPANSION_RATE);
}
value = coerceValueTypesIfAble(type, value);
//type check
if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) {
char buffer[key->info.length + 256];
sprintf(buffer, "Incorrect value type in declaration of '%.*s' (expected %s, got %s)", key->info.length, key->leaf.data, Toy_getValueTypeAsCString(type), Toy_getValueTypeAsCString(value.type));
Toy_error(buffer);
return;
}
probeAndInsert(scope, Toy_copyString(key), value, type, constant);
}
void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
if (entryPtr == NULL) {
char buffer[key->info.length + 256];
sprintf(buffer, "Undefined variable: %s\n", key->leaf.data);
Toy_error(buffer);
return;
}
value = coerceValueTypesIfAble(entryPtr->type, value);
//type check
if (entryPtr->type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && entryPtr->type != value.type && value.type != TOY_VALUE_REFERENCE) {
char buffer[key->info.length + 256];
sprintf(buffer, "Incorrect value type in assignment of '%.*s' (expected %s, got %s)", key->info.length, key->leaf.data, Toy_getValueTypeAsCString(entryPtr->type), Toy_getValueTypeAsCString(value.type));
Toy_error(buffer);
return;
}
//constness check
if (entryPtr->constant) {
char buffer[key->info.length + 256];
sprintf(buffer, "Can't assign to a constant variable %s", key->leaf.data);
Toy_error(buffer);
return;
}
Toy_freeValue(entryPtr->value);
entryPtr->value = value;
}
Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
if (entryPtr == NULL) {
char buffer[key->info.length + 256];
sprintf(buffer, "Undefined variable: %s\n", key->leaf.data);
Toy_error(buffer);
return NULL;
}
return &(entryPtr->value);
}
bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) {
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
return entryPtr != NULL;
}
void Toy_private_incrementScopeRefCount(Toy_Scope* scope) {
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
//check for issues
if (iter->next != NULL && iter->next->refCount == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope's ancestor has a refcount of 0'\n" TOY_CC_RESET);
exit(-1);
}
iter->refCount++;
}
}
void Toy_private_decrementScopeRefCount(Toy_Scope* scope) {
for (Toy_Scope* iter = scope; iter != NULL; iter = iter->next) {
iter->refCount--;
//clean up our insides if needed
if (iter->refCount == 0) {
//free the data
if (iter->data != NULL) {
for (unsigned int i = 0; i < iter->capacity; i++) {
if (iter->data[i].key != NULL) {
Toy_freeString(iter->data[i].key);
Toy_freeValue(iter->data[i].value);
}
}
free(iter->data);
}
}
}
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);
}
}
+47 -16
View File
@@ -1,25 +1,56 @@
#pragma once
#include "toy_literal_array.h"
#include "toy_literal_dictionary.h"
#include "toy_common.h"
#include "toy_bucket.h"
#include "toy_string.h"
#include "toy_value.h"
//keys are leaf-only strings
typedef struct Toy_ScopeEntry {
Toy_String* key;
Toy_Value value;
Toy_ValueType type;
unsigned int psl; //psl '0' means empty
bool constant;
} Toy_ScopeEntry;
//holds a table-like collection of variables TODO: check bitness
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
struct Toy_Scope* next;
unsigned int refCount;
Toy_ScopeEntry* data;
unsigned int capacity;
unsigned int count;
unsigned int maxPsl;
} Toy_Scope;
Toy_Scope* Toy_pushScope(Toy_Scope* scope);
Toy_Scope* Toy_popScope(Toy_Scope* scope);
Toy_Scope* Toy_copyScope(Toy_Scope* original);
//handle deep scopes - the scope is stored in the bucket, not the table
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
//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);
//manage the contents
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant);
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
//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_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
//manage refcounting
void Toy_private_incrementScopeRefCount(Toy_Scope* scope);
void Toy_private_decrementScopeRefCount(Toy_Scope* scope);
//some useful sizes, could be swapped out as needed
#ifndef TOY_SCOPE_INITIAL_CAPACITY
#define TOY_SCOPE_INITIAL_CAPACITY 8
#endif
//NOTE: The DOOM hack needs a power of 2
#ifndef TOY_SCOPE_EXPANSION_RATE
#define TOY_SCOPE_EXPANSION_RATE 2
#endif
//expand when the contents passes a certain percentage (80%) of the capacity
#ifndef TOY_SCOPE_EXPANSION_THRESHOLD
#define TOY_SCOPE_EXPANSION_THRESHOLD 0.7f
#endif
+110
View File
@@ -0,0 +1,110 @@
#include "toy_stack.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Toy_Stack* Toy_allocateStack(void) {
Toy_Stack* stack = malloc(TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack));
if (stack == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, TOY_STACK_INITIAL_CAPACITY, (int)(TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack)));
exit(1);
}
stack->capacity = TOY_STACK_INITIAL_CAPACITY;
stack->count = 0;
return stack;
}
void Toy_freeStack(Toy_Stack* stack) {
if (stack != NULL) {
//if some values will be removed, free them first
for (unsigned int i = 0; i < stack->count; i++) {
Toy_freeValue(stack->data[i]);
}
free(stack);
}
}
void Toy_resetStack(Toy_Stack** stackHandle) {
if ((*stackHandle) == NULL) {
return;
}
//if some values will be removed, free them first
for (unsigned int i = 0; i < (*stackHandle)->count; i++) {
Toy_freeValue((*stackHandle)->data[i]);
}
//reset to the stack's default state
if ((*stackHandle)->capacity > TOY_STACK_INITIAL_CAPACITY) {
(*stackHandle) = realloc((*stackHandle), TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack));
memset((*stackHandle), 0, TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack));
(*stackHandle)->capacity = TOY_STACK_INITIAL_CAPACITY;
}
(*stackHandle)->count = 0;
}
void Toy_pushStack(Toy_Stack** stackHandle, Toy_Value value) {
//don't go overboard
if ((*stackHandle)->count >= TOY_STACK_OVERFLOW_THRESHOLD) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack overflow\n" TOY_CC_RESET);
exit(-1);
}
//expand the capacity if needed
if ((*stackHandle)->count + 1 > (*stackHandle)->capacity) {
while ((*stackHandle)->count + 1 > (*stackHandle)->capacity) {
(*stackHandle)->capacity = (*stackHandle)->capacity < TOY_STACK_INITIAL_CAPACITY ? TOY_STACK_INITIAL_CAPACITY : (*stackHandle)->capacity * TOY_STACK_EXPANSION_RATE;
}
unsigned int newCapacity = (*stackHandle)->capacity;
(*stackHandle) = realloc((*stackHandle), newCapacity * sizeof(Toy_Value) + sizeof(Toy_Stack));
if ((*stackHandle) == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reallocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, (int)newCapacity, (int)(newCapacity * sizeof(Toy_Value) + sizeof(Toy_Stack)));
exit(1);
}
}
//Note: "pointer arithmetic in C/C++ is type-relative"
((Toy_Value*)((*stackHandle) + 1))[(*stackHandle)->count++] = value;
}
Toy_Value Toy_peekStack(Toy_Stack** stackHandle) {
if ((*stackHandle)->count == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack underflow when peeking\n" TOY_CC_RESET);
exit(-1);
}
return ((Toy_Value*)((*stackHandle) + 1))[(*stackHandle)->count - 1];
}
Toy_Value Toy_popStack(Toy_Stack** stackHandle) {
if ((*stackHandle)->count == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack underflow when popping\n" TOY_CC_RESET);
exit(-1);
}
//shrink if possible
if ((*stackHandle)->capacity > TOY_STACK_INITIAL_CAPACITY && (*stackHandle)->count <= (*stackHandle)->capacity / TOY_STACK_CONTRACTION_THRESHOLD) {
(*stackHandle)->capacity /= TOY_STACK_CONTRACTION_THRESHOLD;
unsigned int newCapacity = (*stackHandle)->capacity; //cache for the msg
(*stackHandle) = realloc((*stackHandle), (*stackHandle)->capacity * sizeof(Toy_Value) + sizeof(Toy_Stack));
if ((*stackHandle) == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reallocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, (int)newCapacity, (int)(newCapacity * sizeof(Toy_Value) + sizeof(Toy_Stack)));
exit(1);
}
}
return ((Toy_Value*)((*stackHandle) + 1))[--(*stackHandle)->count];
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "toy_common.h"
#include "toy_value.h"
typedef struct Toy_Stack { //32 | 64 BITNESS
unsigned int capacity; //4 | 4
unsigned int count; //4 | 4
Toy_Value data[]; //- | -
} Toy_Stack; //8 | 8
TOY_API Toy_Stack* Toy_allocateStack(void);
TOY_API void Toy_freeStack(Toy_Stack* stack);
TOY_API void Toy_resetStack(Toy_Stack** stackHandle);
TOY_API void Toy_pushStack(Toy_Stack** stackHandle, Toy_Value value);
TOY_API Toy_Value Toy_peekStack(Toy_Stack** stackHandle);
TOY_API Toy_Value Toy_popStack(Toy_Stack** stackHandle);
//some useful sizes, could be swapped out as needed
#ifndef TOY_STACK_INITIAL_CAPACITY
#define TOY_STACK_INITIAL_CAPACITY 8
#endif
#ifndef TOY_STACK_EXPANSION_RATE
#define TOY_STACK_EXPANSION_RATE 2
#endif
#ifndef TOY_STACK_CONTRACTION_THRESHOLD
#define TOY_STACK_CONTRACTION_THRESHOLD 4 // this integer means 'shrink when count drops below one-forth of capacity'
#endif
//prevent an infinite expansion, limited to 1MB
#ifndef TOY_STACK_OVERFLOW_THRESHOLD
#define TOY_STACK_OVERFLOW_THRESHOLD (1024 * 1024 / sizeof(Toy_Value) - sizeof(Toy_Stack))
#endif
+269
View File
@@ -0,0 +1,269 @@
#include "toy_string.h"
#include "toy_console_colors.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//utils
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
static void incrementRefCount(Toy_String* str) {
str->info.refCount++;
if (str->info.type == TOY_STRING_NODE) {
incrementRefCount(str->node.left);
incrementRefCount(str->node.right);
}
}
static void decrementRefCount(Toy_String* str) {
str->info.refCount--;
if (str->info.type == TOY_STRING_NODE) {
//the parent of this node triggered a decrement across the whole tree
decrementRefCount(str->node.left);
decrementRefCount(str->node.right);
}
if (str->info.refCount == 0) {
if (str->info.type == TOY_STRING_NODE) {
//THIS node has triggered the decrement, so run this again
decrementRefCount(str->node.left);
decrementRefCount(str->node.right);
}
//mark this memory as unused
Toy_releaseBucketPartition((void*)str);
}
}
//exposed functions
Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring) {
return Toy_toStringLength(bucketHandle, cstring, strlen(cstring));
}
Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) {
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String));
ret->info.type = TOY_STRING_LEAF;
ret->info.length = length;
ret->info.refCount = 1;
ret->info.cachedHash = 0; //don't calc until needed
ret->leaf.data = cstring; //don't make a local copy
return ret;
}
Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) {
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + length + 1);
if (length > 0) {
ret->leaf.data = (char*)(ret + 1); //increments by 1 'string', to the length +1
strncpy((char*)(ret->leaf.data), cstring, length);
((char*)(ret->leaf.data))[length] = '\0'; //don't forget the null
ret->info.length = length;
}
else {
ret->leaf.data = "";
ret->info.length = length;
}
ret->info.type = TOY_STRING_LEAF;
ret->info.refCount = 1;
ret->info.cachedHash = 0; //don't calc until needed
return ret;
}
Toy_String* Toy_copyString(Toy_String* str) {
assert(str->info.refCount != 0 && "Can't copy a string with refcount of zero");
incrementRefCount(str);
return str;
}
Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right) {
assert(left->info.refCount != 0 && right->info.refCount != 0 && "Can't concatenate a string with a refcount of zero");
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String));
ret->info.type = TOY_STRING_NODE;
ret->info.length = left->info.length + right->info.length;
ret->info.refCount = 1;
ret->info.cachedHash = 0; //don't calc until needed
ret->node.left = left;
ret->node.right = right;
incrementRefCount(left);
incrementRefCount(right);
return ret;
}
void Toy_freeString(Toy_String* str) {
assert(str->info.refCount > 0 && "Can't free a string with refcount 0");
//memory is freed when the bucket is
decrementRefCount(str);
}
unsigned int Toy_getStringLength(Toy_String* str) {
return str->info.length;
}
unsigned int Toy_getStringRefCount(Toy_String* str) {
return str->info.refCount;
}
static void getStringRawUtil(char* dest, Toy_String* str) {
//sometimes, "clever" can be a bad thing...
if (str->info.type == TOY_STRING_NODE) {
getStringRawUtil(dest, str->node.left);
getStringRawUtil(dest + str->node.left->info.length, str->node.right);
}
else {
memcpy(dest, str->leaf.data, str->info.length);
}
}
char* Toy_getStringRaw(Toy_String* str) {
assert(str->info.refCount != 0 && "Can't build a raw string from a string with refcount of zero");
//BUGFIX: Make sure it's aligned, and there's space for the null
unsigned int len = (str->info.length + 3) & ~3;
if (len == str->info.length) { //nulls aren't counted in a string's length
len += 4;
}
char* buffer = malloc(len);
getStringRawUtil(buffer, str);
buffer[str->info.length] = '\0';
return buffer;
}
static int deepCompareUtil(Toy_String* left, Toy_String* right, const char** leftHead, const char** rightHead) {
//NOTE: this function can't handle strings of zero length
int result = 0;
//if it's the same object, of course they match
if (left == right) {
return result;
}
//BUGFIX: if we're not currently iterating through the left leaf (and leftHead is not null), skip out
if (left->info.type == TOY_STRING_LEAF && (*leftHead) != NULL && (**leftHead) != '\0' && ((*leftHead) < left->leaf.data || (*leftHead) > (left->leaf.data + left->info.length)) ) {
return result;
}
//BUGFIX: if we're not currently iterating through the right leaf (and rightHead is not null), skip out
if (right->info.type == TOY_STRING_LEAF && (*rightHead) != NULL && (**rightHead) != '\0' && ((*rightHead) < right->leaf.data || (*rightHead) > (right->leaf.data + right->info.length)) ) {
return result;
}
//dig into left
if (left->info.type == TOY_STRING_NODE) {
if ((result = deepCompareUtil(left->node.left, right, leftHead, rightHead)) != 0) {
return result;
}
if ((result = deepCompareUtil(left->node.right, right, leftHead, rightHead)) != 0) {
return result;
}
//return zero to keep going
return result;
}
//dig into right
if (right->info.type == TOY_STRING_NODE) {
if ((result = deepCompareUtil(left, right->node.left, leftHead, rightHead)) != 0) {
return result;
}
if ((result = deepCompareUtil(left, right->node.right, leftHead, rightHead)) != 0) {
return result;
}
//return zero to keep going
return result;
}
//keep comparing the leaves
if (left->info.type == TOY_STRING_LEAF && right->info.type == TOY_STRING_LEAF) {
//initial head states can be null, or null characters
if ((*leftHead) == NULL || (**leftHead) == '\0') {
(*leftHead) = left->leaf.data;
}
if ((*rightHead) == NULL || (**rightHead) == '\0') {
(*rightHead) = right->leaf.data;
}
//compare and increment
while (**leftHead && (**leftHead == **rightHead)) {
(*leftHead)++;
(*rightHead)++;
}
//if there's a difference, and neither is null, than a result has (probably) been found
if ((**leftHead != '\0' && **rightHead != '\0' && (**leftHead != **rightHead))) {
result = *(const unsigned char*)(*leftHead) - *(const unsigned char*)(*rightHead);
}
}
//returning 0 means no difference found yet
return result;
}
int Toy_compareStrings(Toy_String* left, Toy_String* right) {
//BUGFIX: since deepCompareUtil() can't handle strings of length zero, insert a check here
if (left->info.length == 0 || right->info.length == 0) {
return left->info.length - right->info.length;
}
//BUGFIX: If both args are leaves, and one is a substring of the other, then deepCompareUtil() will return a wrong result
if (left->info.type == TOY_STRING_LEAF && right->info.type == TOY_STRING_LEAF) {
unsigned int maxLength = left->info.length > right->info.length ? left->info.length : right->info.length;
return strncmp(left->leaf.data, right->leaf.data, maxLength);
}
//util pointers
const char* leftHead = NULL;
const char* rightHead = NULL;
int result = deepCompareUtil(left, right, &leftHead, &rightHead);
//BUGFIX: deepCompareUtil() doesn't handle substrings correctly
if (result == 0 && leftHead != NULL && rightHead != NULL) {
return (int)(*leftHead - *rightHead);
}
else {
return result;
}
}
static unsigned int hashCString(const char* string) {
unsigned int hash = 2166136261u;
for (unsigned int i = 0; string[i]; i++) {
hash *= string[i];
hash ^= 16777619;
}
return hash;
}
unsigned int Toy_hashString(Toy_String* str) {
if (str->info.cachedHash != 0) {
return str->info.cachedHash;
}
else if (str->info.type == TOY_STRING_NODE) {
char* buffer = Toy_getStringRaw(str);
str->info.cachedHash = hashCString(buffer);
free(buffer);
}
else {
str->info.cachedHash = hashCString(str->leaf.data);
}
return str->info.cachedHash;
}

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