Compare commits
247 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d18ff4ba3 | |||
| 30b068fcdb | |||
| 3d8ce4e7d8 | |||
| e0ab4106fa | |||
| 2c143a8be5 | |||
| 0aa6e4063b | |||
| ec39f099ca | |||
| 4dcc05e796 | |||
| 2af95ec82e | |||
| bbdb521333 | |||
| 56987bc96a | |||
| 8498864dde | |||
| 14710dec90 | |||
| d14177dbca | |||
| 42580bbe2a | |||
| 0c8e036de8 | |||
| a55338d8e3 | |||
| 5d240f85a6 | |||
| cceefa6375 | |||
| 632ed7c089 | |||
| c1528f5501 | |||
| 6c5d952c44 | |||
| 208ad9d615 | |||
| 876aad853c | |||
| 1baa65cc95 | |||
| e01e096188 | |||
| 78ce3bdb99 | |||
| bd348abf32 | |||
| 168369d897 | |||
| 8b04939430 | |||
| 8a8074ee6e | |||
| 5686677383 | |||
| bd4ab2aa04 | |||
| 8ce7dd0d95 | |||
| ca24c4f211 | |||
| 016ab9c5fe | |||
| 8072beb007 | |||
| 99bcffe77c | |||
| 5c7e8bba0d | |||
| 3460967e3b | |||
| 467cd8d978 | |||
| 50cc5ca270 | |||
| 256538e1f9 | |||
| 976a9073f8 | |||
| aa44d5fd43 | |||
| f745470abe | |||
| 04771b1f81 | |||
| 9969cbe11a | |||
| 48dbbfaa24 | |||
| d77711da7b | |||
| 57e7acf97a | |||
| 32aad5eecc | |||
| ecbfcc28b5 | |||
| 2b0b678e25 | |||
| 6a086395be | |||
| 2458996ee7 | |||
| 7a15e645a7 | |||
| 73626da5be | |||
| c680427b1e | |||
| 64cc514171 | |||
| 978e7cfac4 | |||
| 9c790f8cd6 | |||
| 7eb16e51bb | |||
| bb6e850548 | |||
| b7f63ea571 | |||
| ac37a017bb | |||
| 29b1c41064 | |||
| 0f6be5ead7 | |||
| 2d171dd664 | |||
| acb5cc90a9 | |||
| 95ba865cab | |||
| 5b420e0799 | |||
| cbc937884e | |||
| 60ddd151a6 | |||
| a7ca6d23d7 | |||
| d3c085c300 | |||
| f2443fbde0 | |||
| 22d0fe596a | |||
| 4b99ce2fb6 | |||
| f8747d8707 | |||
| a0ea0f7f31 | |||
| 5861602f23 | |||
| 8550f3141c | |||
| 8d8928438d | |||
| 28b1b8e2cc | |||
| 9a55ff221a | |||
| 8f61575579 | |||
| 23fdec541d | |||
| 4137b7f057 | |||
| 6c71a16e3e | |||
| 6511d652f2 | |||
| 39af340fbc | |||
| 63a7231254 | |||
| d97fd3fc22 | |||
| 893e435023 | |||
| 17f1dc8647 | |||
| b8f20add66 | |||
| 304e1a5fb0 | |||
| 82c03ecb33 | |||
| 3d8871abe1 | |||
| 2aecf6e8a1 | |||
| 2a3206d951 | |||
| f80709ae41 | |||
| 33302ef318 | |||
| 9ce5f6e67e | |||
| 7fb9ebbce0 | |||
| dceb83e618 | |||
| fb1ac71f42 | |||
| 54aacff26e | |||
| ae270008b0 | |||
| 22af1edb1d | |||
| ac35a859e0 | |||
| cc27da50df | |||
| 6787cfff55 | |||
| 36154b25ac | |||
| d12ad4f60d | |||
| 86061cb74e | |||
| 32d6b7124c | |||
| c039045c14 | |||
| e6fe42c0ca | |||
| 672d68a73f | |||
| 1b8559f0ef | |||
| 4ad33a3082 | |||
| d2aacea8c5 | |||
| 6c151e21b0 | |||
| 4625efecfd | |||
| c58c8911fe | |||
| 1f6b3e232d | |||
| 90d18c7520 | |||
| ea3ca2751c | |||
| 90b504d3a4 | |||
| 3a4017cea5 | |||
| 14718a08d3 | |||
| 1c6c7e5d4f | |||
| 08c8e7e3e6 | |||
| d055e9dc94 | |||
| a6f0200255 | |||
| 61efb96fe2 | |||
| 1937d727bb | |||
| 4fb2dea1b4 | |||
| f705d82aee | |||
| 71ff481f6c | |||
| 5300e2ceec | |||
| 9c766ec61e | |||
| 3355c3a4c2 | |||
| ec91bac8a5 | |||
| e523a6f60a | |||
| 90e5a5d08b | |||
| 85f3dfbf1d | |||
| 151f681954 | |||
| 7bd67765aa | |||
| f36289786e | |||
| ffc50ceafb | |||
| 0c67ce6476 | |||
| a4f1e048e9 | |||
| 041fe99e01 | |||
| 4f70bea808 | |||
| 6939b216a9 | |||
| ce2073832b | |||
| 08e2adab50 | |||
| b675c4c1bd | |||
| ae32a2e7ae | |||
| 2f538e79fe | |||
| ebe1d712c9 | |||
| 5b82ed8e45 | |||
| 300f8e382b | |||
| 45920f763c | |||
| 0174deb08a | |||
| 9ac16f8e80 | |||
| df6bd58b1a | |||
| 9c995830e2 | |||
| dc1914b9ed | |||
| b385b461e0 | |||
| c64d451287 | |||
| 80ccd119ff | |||
| 18ecece348 | |||
| 18c5fb6add | |||
| daceaa5492 | |||
| f5e060051e | |||
| 975ed41d14 | |||
| cbdfcbcc14 | |||
| 8309535bbe | |||
| 2bf721867b | |||
| 8eca419138 | |||
| 596a4882bc | |||
| 97e72550da | |||
| 4f0aebc32f | |||
| 9365541c8f | |||
| 16291bd1af | |||
| 574a864dd0 | |||
| bde07f6c12 | |||
| 42716b24c9 | |||
| ecca17dbef | |||
| 7e01ead6ff | |||
| 6b834ca6d1 | |||
| 8d12db7dbe | |||
| 4cda75df11 | |||
| 9e899f5974 | |||
| 4aa6f75ea7 | |||
| e9ab6f3f96 | |||
| 633df5f376 | |||
| 74e2907f09 | |||
| 3098d75d01 | |||
| b80888a7bb | |||
| afad0d99cb | |||
| 55aa6eb273 | |||
| 7424a681d6 | |||
| 53f0996fd1 | |||
| 998b913fc9 | |||
| 2f18989f25 | |||
| 80b64cf21e | |||
| 42e55c1db6 | |||
| 00812e7a73 | |||
| 1430aefdf3 | |||
| 46097000a8 | |||
| 5c93bf663a | |||
| 32ac46d9a0 | |||
| 52651c5f2c | |||
| 603d9d2b06 | |||
| 380b7a3699 | |||
| 67f7b3e436 | |||
| 6a883bde96 | |||
| 9603baeb0a | |||
| 6d5549fc8e | |||
| 08ce270e06 | |||
| cf8b3975c0 | |||
| 6f4bfc0e10 | |||
| 3fb952e3c2 | |||
| ceeca9d40f | |||
| 3b89e216cc | |||
| 072d9c59cc | |||
| 9a415738d9 | |||
| d7fda480fd | |||
| 7ff232c814 | |||
| a05b93975e | |||
| c178824a0a | |||
| 3cdf77b85c | |||
| 7a3986af33 | |||
| 0048c92cf5 | |||
| 6b33895f75 | |||
| 577d1965cb | |||
| bfaf4e83bb | |||
| 8f2ba5cd50 | |||
| cd05d5d84a | |||
| 1ff32fe101 | |||
| 3cad70dddd | |||
| 3cbf7b13eb |
@@ -0,0 +1,19 @@
|
|||||||
|
name: Comprehensive Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: install valgrind
|
||||||
|
run: sudo apt install valgrind
|
||||||
|
- name: make test
|
||||||
|
run: make test
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#Editor generated files
|
||||||
|
*.sln
|
||||||
|
*.vcproj
|
||||||
|
*.suo
|
||||||
|
*.ncb
|
||||||
|
*.user
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
#Directories
|
||||||
|
Release/
|
||||||
|
Debug/
|
||||||
|
Out/
|
||||||
|
release/
|
||||||
|
debug/
|
||||||
|
out/
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
#Project generated files
|
||||||
|
*.db
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.exe
|
||||||
|
*.meta
|
||||||
|
*.log
|
||||||
|
out
|
||||||
|
*.stackdump
|
||||||
|
*.tb
|
||||||
|
|
||||||
|
#Shell files
|
||||||
|
*.bat
|
||||||
|
*.sh
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
@@ -1,4 +1,71 @@
|
|||||||
This git branch is the (now deprecated) documentation website for The Toy Programming Language version 2.x, which can be found at [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
|
<p align="center">
|
||||||
|
<image src="toylogo.png" />
|
||||||
|
</p>
|
||||||
|
|
||||||
Toy v2.x is still under active development, so this documentation will change and evolve over time, and may not reflect the current reference implementation.
|
# Toy
|
||||||
|
|
||||||
|
This is the Toy programming language interpreter, written in C.
|
||||||
|
|
||||||
|
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
|
||||||
|
|
||||||
|
# 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` and `export` variables from the host program
|
||||||
|
* Fancy slice notation for strings, arrays and dictionaries
|
||||||
|
* Can re-direct output, error and assertion failure messages
|
||||||
|
* Open source under the zlib license
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
For Windows, Linux and MacOS, simply run `make` in the root directory.
|
||||||
|
|
||||||
|
Note: For Linux, you may need to `cd` into the `out` directory before running.
|
||||||
|
|
||||||
|
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
Run `make install-tools` to install a number of tools, including:
|
||||||
|
|
||||||
|
* VSCode syntax highlighting
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
import standard; //for a bunch of utility functions
|
||||||
|
|
||||||
|
|
||||||
|
print "Hello world"; //"print" is a keyword
|
||||||
|
|
||||||
|
var msg = "foobar"; //declare a variable like this
|
||||||
|
|
||||||
|
assert true, "This message won't be seen"; //assert is another keyword
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
fn makeCounter() { //declare a function like this
|
||||||
|
var total: int = 0; //declare a variable with a type like this
|
||||||
|
|
||||||
|
fn counter(): int { //declare a return type like this
|
||||||
|
return ++total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter; //closures are explicitly supported
|
||||||
|
}
|
||||||
|
|
||||||
|
var tally = makeCounter();
|
||||||
|
|
||||||
|
print tally(); //1
|
||||||
|
print tally(); //2
|
||||||
|
print tally(); //3
|
||||||
|
|
||||||
|
export tally; //export this variable to the host program
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
title: The Toy Programming Language
|
|
||||||
description: Documentation For The Toy Programming Language
|
|
||||||
keywords: programming,coding
|
|
||||||
author: Kayne Ruse (Ratstail91)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<!-- Google tag (gtag.js) -->
|
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-WQ8Q1JV8E8"></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
gtag('config', 'G-WQ8Q1JV8E8');
|
|
||||||
</script>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<!-- site information -->
|
|
||||||
<meta name="description" content="{{ site.description }}" />
|
|
||||||
<meta name="author" content="{{ site.author }}" />
|
|
||||||
<meta name="keywords" content="{{ site.keywords }}" />
|
|
||||||
|
|
||||||
<!-- facebook -->
|
|
||||||
<meta property="og:url" content="{{ site.url }}" />
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:image" content="{{ site.baseurl }}/assets/repo-preview.png" />
|
|
||||||
<meta property="og:title" content="{{ page.title }}" />
|
|
||||||
<meta property="og:description" content="{{ page.description }}" />
|
|
||||||
|
|
||||||
<!-- twitter -->
|
|
||||||
<meta name="twitter:card" content="{{ site.title }}" />
|
|
||||||
<meta name="twitter:url" content="{{ site.url}}" />
|
|
||||||
<meta name="twitter:type" content="website" />
|
|
||||||
<meta name="twitter:image" content="{{ site.baseurl }}/assets/repo-preview.png" />
|
|
||||||
<meta name="twitter:title" content="{{ page.title }}" />
|
|
||||||
<meta name="twitter:description" content="{{ page.description }}" />
|
|
||||||
|
|
||||||
<link rel="icon" href="{{ site.baseurl }}/favicon.png">
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<!-- device settings -->
|
|
||||||
<meta charset = "UTF-8" />
|
|
||||||
<meta name="Content-Type" content="text/html" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
|
|
||||||
<!-- page title -->
|
|
||||||
<title>{{ page.title }}</title>
|
|
||||||
|
|
||||||
{% include metadata.html %}
|
|
||||||
{% include analytics.html %}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{{ content }}
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 454 KiB |
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<browserconfig>
|
|
||||||
<msapplication>
|
|
||||||
<tile>
|
|
||||||
<square150x150logo src="/mstile-150x150.png"/>
|
|
||||||
<TileColor>#da532c</TileColor>
|
|
||||||
</tile>
|
|
||||||
</msapplication>
|
|
||||||
</browserconfig>
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
DONE: rework type system
|
||||||
|
DONE: var decl with a type, but no value
|
||||||
|
DONE: type casting
|
||||||
|
DONE: remove optimization option
|
||||||
|
DONE: conditionals
|
||||||
|
DONE: if-then-else
|
||||||
|
DONE: chained if-then-else
|
||||||
|
DONE: optional block around a path if it's only one statement
|
||||||
|
DONE: while-then
|
||||||
|
DONE: for-then
|
||||||
|
DONE: break and continue statements
|
||||||
|
DONE: truthiness rethink
|
||||||
|
DONE: string concat with the + operator
|
||||||
|
DONE: increment & decrement operators
|
||||||
|
DONE: store compound types in variables
|
||||||
|
DONE: += -= *= /= %= operators
|
||||||
|
DONE: && and || operators
|
||||||
|
DONE: functions are invoked by calling their names
|
||||||
|
DONE: function arguments can have specified types
|
||||||
|
DONE: function returns can have specified types
|
||||||
|
DONE: closures are explicitly supported
|
||||||
|
DONE: functions are first-class citizens
|
||||||
|
DONE: functions take a set number of parameters
|
||||||
|
DONE: functions last argument can be a rest parameter
|
||||||
|
DONE: assert needs to kill the whole script, not just functions
|
||||||
|
DONE: native functions
|
||||||
|
DONE: global functions _get, _set, _push, _pop, _length, clear available
|
||||||
|
DONE: change comma to colon in dictionary definition
|
||||||
|
DONE: Address circular references
|
||||||
|
DONE: are compounds shallow or deep copies? Deep copies
|
||||||
|
DONE: third output stream, for lexer/parser/compiler/interpreter errors
|
||||||
|
DONE: Assertion-based test scripts
|
||||||
|
DONE: Import/export keywords
|
||||||
|
DONE: A way to check the type of a variable (typeOf keyword)
|
||||||
|
DONE: slice and dot notation around the builtin _index and _dot functions
|
||||||
|
DONE: maximum recursion/function depth
|
||||||
|
DONE: better sugar for _push, _pop, _length
|
||||||
|
DONE: nested compound assignment bug
|
||||||
|
DONE: hooks on the external libraries, triggered on import
|
||||||
|
|
||||||
|
|
||||||
|
TODO: standard library
|
||||||
|
TODO: external script runner library
|
||||||
|
TODO: document how it all works
|
||||||
|
TODO: packaging for release?
|
||||||
|
TODO: test embedding in a game
|
||||||
|
|
||||||
|
NOPE: a = b = c = 1;
|
||||||
|
NOPE: functions return a set number of values
|
||||||
|
NOPE: ternary operator?
|
||||||
|
NOPE: Nullish types?
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
layout: page
|
|
||||||
title: The Toy Programming Language
|
|
||||||
---
|
|
||||||
|
|
||||||
<div style="justify-self: center;">
|
|
||||||
<image src="assets/toylogo.png" width="250" height="250" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="justify-self: center;">
|
|
||||||
<a href="https://github.com/krgamestudios/Toy"><img src="https://github.com/krgamestudios/Toy/actions/workflows/continuous-integration-v2.yml/badge.svg"></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
The documentation on this website is under construction, for further information, see the repository: [https://gitea.krgamestudios.com/krgamestudios/Toy](https://gitea.krgamestudios.com/krgamestudios/Toy), or the GitHub mirror: [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
|
|
||||||
|
|
||||||
An example of Toy in action: [Vampire Toyvivors](https://gitea.krgamestudios.com/krgamestudios/VampireToyvivors).
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Optimisation Options
|
||||||
|
# export CFLAGS+=-O2 -mtune=native -march=native
|
||||||
|
|
||||||
|
export TOY_OUTDIR = out
|
||||||
|
|
||||||
|
all: $(TOY_OUTDIR) repl
|
||||||
|
|
||||||
|
repl: $(TOY_OUTDIR) library
|
||||||
|
$(MAKE) -C repl
|
||||||
|
|
||||||
|
repl-static: $(TOY_OUTDIR) static
|
||||||
|
$(MAKE) -C repl
|
||||||
|
|
||||||
|
library: $(TOY_OUTDIR)
|
||||||
|
$(MAKE) -j8 -C source library
|
||||||
|
|
||||||
|
static: $(TOY_OUTDIR)
|
||||||
|
$(MAKE) -j8 -C source static
|
||||||
|
|
||||||
|
test: clean $(TOY_OUTDIR)
|
||||||
|
$(MAKE) -C test
|
||||||
|
|
||||||
|
$(TOY_OUTDIR):
|
||||||
|
mkdir $(TOY_OUTDIR)
|
||||||
|
|
||||||
|
#utils
|
||||||
|
install-tools:
|
||||||
|
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
|
||||||
|
|
||||||
|
.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
|
||||||
|
else ifeq ($(OS),Windows_NT)
|
||||||
|
$(RM) *.o *.a *.exe
|
||||||
|
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
|
||||||
|
else
|
||||||
|
@echo "Deletion failed - what platform is this?"
|
||||||
|
endif
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
Before Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,94 @@
|
|||||||
|
#include "lib_standard.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 0) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to clock\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the time from C (what a pain)
|
||||||
|
time_t rawtime = time(NULL);
|
||||||
|
struct tm* timeinfo = localtime( &rawtime );
|
||||||
|
char* timestr = asctime(timeinfo);
|
||||||
|
|
||||||
|
//push to the stack
|
||||||
|
int len = strlen(timestr) - 1; //-1 for the newline
|
||||||
|
Literal timeLiteral = TO_STRING_LITERAL(copyString(timestr, len), len);
|
||||||
|
|
||||||
|
//push to the stack
|
||||||
|
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
char* name;
|
||||||
|
NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"clock", nativeClock},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
freeLiteral(alias);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
|
||||||
|
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||||
|
func.type = LITERAL_FUNCTION_NATIVE;
|
||||||
|
|
||||||
|
setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
freeLiteral(name);
|
||||||
|
freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
||||||
|
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
||||||
|
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(dict);
|
||||||
|
freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||||
|
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
#include "lib_timer.h"
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//god damn it
|
||||||
|
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
|
||||||
|
struct timeval* d = ALLOCATE(struct timeval, 1);
|
||||||
|
|
||||||
|
d->tv_sec = rhs->tv_sec - lhs->tv_sec;
|
||||||
|
d->tv_usec = rhs->tv_usec - lhs->tv_usec;
|
||||||
|
|
||||||
|
if (d->tv_usec < 0) {
|
||||||
|
d->tv_sec--;
|
||||||
|
d->tv_usec += 1000 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 0) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the timeinfo from C
|
||||||
|
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
||||||
|
gettimeofday(timeinfo, NULL);
|
||||||
|
|
||||||
|
//wrap in an opaque literal for Toy
|
||||||
|
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||||
|
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the timeinfo from C
|
||||||
|
struct timeval timerStop;
|
||||||
|
gettimeofday(&timerStop, NULL);
|
||||||
|
|
||||||
|
//unwrap the opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//determine the difference, and wrap it
|
||||||
|
struct timeval* d = diff(timerStart, &timerStop);
|
||||||
|
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(diffLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 2) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the args
|
||||||
|
Literal microsecondLiteral = popLiteralArray(arguments);
|
||||||
|
Literal secondLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal secondLiteralIdn = secondLiteral;
|
||||||
|
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
|
||||||
|
freeLiteral(secondLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal microsecondLiteralIdn = microsecondLiteral;
|
||||||
|
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, µsecondLiteral)) {
|
||||||
|
freeLiteral(microsecondLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
|
||||||
|
freeLiteral(secondLiteral);
|
||||||
|
freeLiteral(microsecondLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the timeinfo from toy
|
||||||
|
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
|
||||||
|
timeinfo->tv_sec = AS_INTEGER(secondLiteral);
|
||||||
|
timeinfo->tv_usec = AS_INTEGER(microsecondLiteral);
|
||||||
|
|
||||||
|
//wrap in an opaque literal for Toy
|
||||||
|
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||||
|
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(secondLiteral);
|
||||||
|
freeLiteral(microsecondLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to getTimerSeconds\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap the opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//create the result literal
|
||||||
|
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
|
||||||
|
pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(result);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to getTimerSeconds\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap the opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//create the result literal
|
||||||
|
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
|
||||||
|
pushLiteralArray(&interpreter->stack, result);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(result);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 2) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap the opaque literals
|
||||||
|
Literal rhsLiteral = popLiteralArray(arguments);
|
||||||
|
Literal lhsLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal lhsLiteralIdn = lhsLiteral;
|
||||||
|
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
|
||||||
|
freeLiteral(lhsLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal rhsLiteralIdn = rhsLiteral;
|
||||||
|
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
|
||||||
|
freeLiteral(rhsLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
|
||||||
|
freeLiteral(lhsLiteral);
|
||||||
|
freeLiteral(rhsLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
|
||||||
|
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
|
||||||
|
|
||||||
|
//determine the difference, and wrap it
|
||||||
|
struct timeval* d = diff(lhsTimer, rhsTimer);
|
||||||
|
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
|
||||||
|
pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(lhsLiteral);
|
||||||
|
freeLiteral(rhsLiteral);
|
||||||
|
freeLiteral(diffLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap in an opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
//create the string literal
|
||||||
|
char buffer[128];
|
||||||
|
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
|
||||||
|
Literal resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
|
||||||
|
|
||||||
|
pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
freeLiteral(resultLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
//no arguments
|
||||||
|
if (arguments->count != 1) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to _desroyTimer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unwrap in an opaque literal
|
||||||
|
Literal timeLiteral = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal timeLiteralIdn = timeLiteral;
|
||||||
|
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_OPAQUE(timeLiteral)) {
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval* timer = AS_OPAQUE(timeLiteral);
|
||||||
|
|
||||||
|
FREE(struct timeval, timer);
|
||||||
|
|
||||||
|
freeLiteral(timeLiteral);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//call the hook
|
||||||
|
typedef struct Natives {
|
||||||
|
char* name;
|
||||||
|
NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
|
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
|
||||||
|
//build the natives list
|
||||||
|
Natives natives[] = {
|
||||||
|
{"startTimer", nativeStartTimer},
|
||||||
|
{"_stopTimer", nativeStopTimer},
|
||||||
|
{"createTimer", nativeCreateTimer},
|
||||||
|
{"_getTimerSeconds", nativeGetTimerSeconds},
|
||||||
|
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
|
||||||
|
{"_compareTimer", nativeCompareTimer},
|
||||||
|
{"_timerToString", nativeTimerToString},
|
||||||
|
{"_destroyTimer", nativeDestroyTimer},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
//store the library in an aliased dictionary
|
||||||
|
if (!IS_NULL(alias)) {
|
||||||
|
//make sure the name isn't taken
|
||||||
|
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||||
|
interpreter->errorOutput("Can't override an existing variable\n");
|
||||||
|
freeLiteral(alias);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the dictionary to load up with functions
|
||||||
|
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//load the dict with functions
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
|
||||||
|
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||||
|
func.type = LITERAL_FUNCTION_NATIVE;
|
||||||
|
|
||||||
|
setLiteralDictionary(dictionary, name, func);
|
||||||
|
|
||||||
|
freeLiteral(name);
|
||||||
|
freeLiteral(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//build the type
|
||||||
|
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
|
||||||
|
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
|
||||||
|
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, strType);
|
||||||
|
TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||||
|
|
||||||
|
//set scope
|
||||||
|
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
declareScopeVariable(interpreter->scope, alias, type);
|
||||||
|
setScopeVariable(interpreter->scope, alias, dict, false);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeLiteral(dict);
|
||||||
|
freeLiteral(type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
for (int i = 0; natives[i].name; i++) {
|
||||||
|
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
CC=gcc
|
||||||
|
|
||||||
|
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)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OBJ): | $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR):
|
||||||
|
mkdir $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR)/%.o: %.c
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(ODIR)
|
||||||
|
rm /usr/local/lib/lib$(OUTNAME).dylib
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
#include "repl_tools.h"
|
||||||
|
#include "lib_standard.h"
|
||||||
|
#include "lib_timer.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void repl() {
|
||||||
|
//repl does it's own thing for now
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
const int size = 2048;
|
||||||
|
char input[size];
|
||||||
|
memset(input, 0, size);
|
||||||
|
|
||||||
|
Interpreter interpreter; //persist the interpreter for the scopes
|
||||||
|
initInterpreter(&interpreter);
|
||||||
|
|
||||||
|
//inject the libs
|
||||||
|
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||||
|
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
printf("> ");
|
||||||
|
fgets(input, size, stdin);
|
||||||
|
|
||||||
|
//escape the repl (length of 5 to accomodate the newline)
|
||||||
|
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//setup this iteration
|
||||||
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
Compiler compiler;
|
||||||
|
|
||||||
|
initLexer(&lexer, input);
|
||||||
|
initParser(&parser, &lexer);
|
||||||
|
initCompiler(&compiler);
|
||||||
|
|
||||||
|
//run this iteration
|
||||||
|
ASTNode* node = scanParser(&parser);
|
||||||
|
while(node != NULL) {
|
||||||
|
//pack up and restart
|
||||||
|
if (node->type == AST_NODEERROR) {
|
||||||
|
printf(ERROR "error node detected\n" RESET);
|
||||||
|
error = true;
|
||||||
|
freeNode(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCompiler(&compiler, node);
|
||||||
|
freeNode(node);
|
||||||
|
node = scanParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
//get the bytecode dump
|
||||||
|
int size = 0;
|
||||||
|
unsigned char* tb = collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
|
//run the bytecode
|
||||||
|
runInterpreter(&interpreter, tb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//clean up this iteration
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeInterpreter(&interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
|
//entry point
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
initCommand(argc, argv);
|
||||||
|
|
||||||
|
//command specific actions
|
||||||
|
if (command.error) {
|
||||||
|
usageCommand(argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.help) {
|
||||||
|
helpCommand(argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.version) {
|
||||||
|
copyrightCommand(argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: remove this when the interpreter meets the specification
|
||||||
|
if (command.verbose) {
|
||||||
|
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
//run source file
|
||||||
|
if (command.sourcefile) {
|
||||||
|
runSourceFile(command.sourcefile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//run from stdin
|
||||||
|
if (command.source) {
|
||||||
|
runSource(command.source);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//compile source file
|
||||||
|
if (command.compilefile && command.outfile) {
|
||||||
|
size_t size = 0;
|
||||||
|
char* source = readFile(command.compilefile, &size);
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
if (!tb) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
writeFile(command.outfile, tb, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//run binary
|
||||||
|
if (command.binaryfile) {
|
||||||
|
runBinaryFile(command.binaryfile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
repl();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#include "repl_tools.h"
|
||||||
|
#include "lib_standard.h"
|
||||||
|
#include "lib_timer.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
//IO functions
|
||||||
|
char* readFile(char* path, size_t* fileSize) {
|
||||||
|
FILE* file = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
*fileSize = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
char* buffer = (char*)malloc(*fileSize + 1);
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||||
|
|
||||||
|
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||||
|
|
||||||
|
if (bytesRead < *fileSize) {
|
||||||
|
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFile(char* path, unsigned char* bytes, size_t size) {
|
||||||
|
FILE* file = fopen(path, "wb");
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int written = fwrite(bytes, size, 1, file);
|
||||||
|
|
||||||
|
if (written != 1) {
|
||||||
|
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
//repl functions
|
||||||
|
unsigned char* compileString(char* source, size_t* size) {
|
||||||
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
Compiler compiler;
|
||||||
|
|
||||||
|
initLexer(&lexer, source);
|
||||||
|
initParser(&parser, &lexer);
|
||||||
|
initCompiler(&compiler);
|
||||||
|
|
||||||
|
//run the parser until the end of the source
|
||||||
|
ASTNode* node = scanParser(&parser);
|
||||||
|
while(node != NULL) {
|
||||||
|
//pack up and leave
|
||||||
|
if (node->type == AST_NODEERROR) {
|
||||||
|
printf(ERROR "error node detected\n" RESET);
|
||||||
|
freeNode(node);
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCompiler(&compiler, node);
|
||||||
|
freeNode(node);
|
||||||
|
node = scanParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the bytecode dump
|
||||||
|
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
//no lexer to clean up
|
||||||
|
|
||||||
|
//finally
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void runBinary(unsigned char* tb, size_t size) {
|
||||||
|
Interpreter interpreter;
|
||||||
|
initInterpreter(&interpreter);
|
||||||
|
|
||||||
|
//inject the libs
|
||||||
|
injectNativeHook(&interpreter, "standard", hookStandard);
|
||||||
|
injectNativeHook(&interpreter, "timer", hookTimer);
|
||||||
|
|
||||||
|
runInterpreter(&interpreter, tb, size);
|
||||||
|
freeInterpreter(&interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void runBinaryFile(char* fname) {
|
||||||
|
size_t size = 0; //not used
|
||||||
|
unsigned char* tb = (unsigned char*)readFile(fname, &size);
|
||||||
|
if (!tb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runBinary(tb, size);
|
||||||
|
//interpreter takes ownership of the binary data
|
||||||
|
}
|
||||||
|
|
||||||
|
void runSource(char* source) {
|
||||||
|
size_t size = 0;
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
if (!tb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runBinary(tb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void runSourceFile(char* fname) {
|
||||||
|
size_t size = 0; //not used
|
||||||
|
char* source = readFile(fname, &size);
|
||||||
|
runSource(source);
|
||||||
|
free((void*)source);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
char* readFile(char* path, size_t* fileSize);
|
||||||
|
void writeFile(char* path, unsigned char* bytes, size_t size);
|
||||||
|
|
||||||
|
unsigned char* compileString(char* source, size_t* size);
|
||||||
|
|
||||||
|
void runBinary(unsigned char* tb, size_t size);
|
||||||
|
void runBinaryFile(char* fname);
|
||||||
|
void runSource(char* source);
|
||||||
|
void runSourceFile(char* fname);
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
|
||||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="454.000000pt" height="454.000000pt" viewBox="0 0 454.000000 454.000000"
|
|
||||||
preserveAspectRatio="xMidYMid meet">
|
|
||||||
<metadata>
|
|
||||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
|
||||||
</metadata>
|
|
||||||
<g transform="translate(0.000000,454.000000) scale(0.100000,-0.100000)"
|
|
||||||
fill="#000000" stroke="none">
|
|
||||||
<path d="M1178 4533 c-3 -5 -4 -508 -3 -1118 l0 -1111 -565 0 -565 0 -3 -1152
|
|
||||||
-2 -1152 2230 0 2230 0 -2 1152 -3 1152 -522 0 c-359 0 -523 3 -524 10 0 6 0
|
|
||||||
509 0 1119 l-1 1107 -1133 0 c-624 0 -1135 -3 -1137 -7z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 730 B |
@@ -0,0 +1,89 @@
|
|||||||
|
//single line comment
|
||||||
|
|
||||||
|
/*
|
||||||
|
multi line comment
|
||||||
|
*/
|
||||||
|
|
||||||
|
//test primitive literals
|
||||||
|
print "hello world";
|
||||||
|
print null;
|
||||||
|
print true;
|
||||||
|
print false;
|
||||||
|
print 42;
|
||||||
|
print 3.14;
|
||||||
|
print -69;
|
||||||
|
print -4.20;
|
||||||
|
print 2 + (3 * 3);
|
||||||
|
|
||||||
|
//test operators (integers)
|
||||||
|
print 1 + 1;
|
||||||
|
print 1 - 1;
|
||||||
|
print 2 * 2;
|
||||||
|
print 1 / 2;
|
||||||
|
print 4 % 2;
|
||||||
|
|
||||||
|
//test operators (floats)
|
||||||
|
print 1.0 + 1.0;
|
||||||
|
print 1.0 - 1.0;
|
||||||
|
print 2.0 * 2.0;
|
||||||
|
print 1.0 / 2.0;
|
||||||
|
|
||||||
|
//test scopes
|
||||||
|
{
|
||||||
|
print "This statement is within a scope.";
|
||||||
|
{
|
||||||
|
print "This is a deeper scope.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print "Back to the outer scope.";
|
||||||
|
|
||||||
|
//test scope will delegate to higher scope
|
||||||
|
var a = 1;
|
||||||
|
{
|
||||||
|
a = 2;
|
||||||
|
print a;
|
||||||
|
}
|
||||||
|
print a;
|
||||||
|
|
||||||
|
//test scope will shadow higher scope on redefine
|
||||||
|
var b: int = 3;
|
||||||
|
{
|
||||||
|
var b = 4;
|
||||||
|
print b;
|
||||||
|
}
|
||||||
|
print b;
|
||||||
|
|
||||||
|
//test compounds, repeatedly
|
||||||
|
print [1, 2, 3];
|
||||||
|
print [4, 5];
|
||||||
|
print ["key":"value"];
|
||||||
|
print [1, 2, 3];
|
||||||
|
print [4, 5];
|
||||||
|
print ["key":"value"];
|
||||||
|
|
||||||
|
//test empties
|
||||||
|
print [];
|
||||||
|
print [:];
|
||||||
|
|
||||||
|
//test nested compounds
|
||||||
|
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
||||||
|
|
||||||
|
//var declarations
|
||||||
|
var x = 31;
|
||||||
|
var y : int = 42;
|
||||||
|
var arr : [int] = [1, 2, 3, 42];
|
||||||
|
var dict : [string:int] = ["hello": 1, "world":2];
|
||||||
|
|
||||||
|
//printing expressions
|
||||||
|
print x;
|
||||||
|
print x + y;
|
||||||
|
print arr;
|
||||||
|
print dict;
|
||||||
|
|
||||||
|
//test asserts at the end of the file
|
||||||
|
assert x, "This won't be seen";
|
||||||
|
assert true, "This won't be seen";
|
||||||
|
assert false, "This is a failed assert, and will end execution";
|
||||||
|
|
||||||
|
print "This will not be printed because of the above assert";
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fn fib(n : int) {
|
||||||
|
if (n < 2) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fib(n-1) + fib(n-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 20; i++) {
|
||||||
|
var res = fib(i);
|
||||||
|
print string i + ": " + string res;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
var size: int const = 100;
|
||||||
|
|
||||||
|
var prev = [];
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
prev.push(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev.set(size - 1, true);
|
||||||
|
|
||||||
|
|
||||||
|
fn calc(p, i) {
|
||||||
|
if (p[i-1] && p[i] && p[i+1]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p[i-1] && p[i] && !p[i+1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p[i-1] && !p[i] && p[i+1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p[i-1] && !p[i] && !p[i+1]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p[i-1] && p[i] && p[i+1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p[i-1] && p[i] && !p[i+1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p[i-1] && !p[i] && p[i+1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p[i-1] && !p[i] && !p[i+1]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (var iteration = 0; iteration < 100; iteration++) {
|
||||||
|
var line = [false];
|
||||||
|
for (var i = 1; i < size-1; i++) {
|
||||||
|
line.push(calc(prev, i));
|
||||||
|
}
|
||||||
|
line.push(false);
|
||||||
|
|
||||||
|
var output = "";
|
||||||
|
for (var i = 0; i < line.length(); i++) {
|
||||||
|
if (line[i]) {
|
||||||
|
output += "*";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print output;
|
||||||
|
prev = line;
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "",
|
|
||||||
"short_name": "",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/android-chrome-192x192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/android-chrome-384x384.png",
|
|
||||||
"sizes": "384x384",
|
|
||||||
"type": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"theme_color": "#ffffff",
|
|
||||||
"background_color": "#ffffff",
|
|
||||||
"display": "standalone"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
#include "ast_node.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void freeNodeCustom(ASTNode* node, bool freeSelf) {
|
||||||
|
//don't free a NULL node
|
||||||
|
if (node == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(node->type) {
|
||||||
|
case AST_NODEERROR:
|
||||||
|
//NO-OP
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODELITERAL:
|
||||||
|
freeLiteral(node->atomic.literal);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEUNARY:
|
||||||
|
freeNode(node->unary.child);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEBINARY:
|
||||||
|
freeNode(node->binary.left);
|
||||||
|
freeNode(node->binary.right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEGROUPING:
|
||||||
|
freeNode(node->grouping.child);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEBLOCK:
|
||||||
|
for (int i = 0; i < node->block.count; i++) {
|
||||||
|
freeNodeCustom(node->block.nodes + i, false);
|
||||||
|
}
|
||||||
|
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODECOMPOUND:
|
||||||
|
for (int i = 0; i < node->compound.count; i++) {
|
||||||
|
freeNodeCustom(node->compound.nodes + i, false);
|
||||||
|
}
|
||||||
|
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEPAIR:
|
||||||
|
freeNode(node->pair.left);
|
||||||
|
freeNode(node->pair.right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEVAR_DECL:
|
||||||
|
freeLiteral(node->varDecl.identifier);
|
||||||
|
freeLiteral(node->varDecl.typeLiteral);
|
||||||
|
freeNode(node->varDecl.expression);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEFN_DECL:
|
||||||
|
freeLiteral(node->fnDecl.identifier);
|
||||||
|
freeNode(node->fnDecl.arguments);
|
||||||
|
freeNode(node->fnDecl.returns);
|
||||||
|
freeNode(node->fnDecl.block);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEFN_COLLECTION:
|
||||||
|
for (int i = 0; i < node->fnCollection.count; i++) {
|
||||||
|
freeNodeCustom(node->fnCollection.nodes + i, false);
|
||||||
|
}
|
||||||
|
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEFN_CALL:
|
||||||
|
freeNode(node->fnCall.arguments);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEPATH_IF:
|
||||||
|
case AST_NODEPATH_WHILE:
|
||||||
|
case AST_NODEPATH_FOR:
|
||||||
|
case AST_NODEPATH_BREAK:
|
||||||
|
case AST_NODEPATH_CONTINUE:
|
||||||
|
case AST_NODEPATH_RETURN:
|
||||||
|
freeNode(node->path.preClause);
|
||||||
|
freeNode(node->path.postClause);
|
||||||
|
freeNode(node->path.condition);
|
||||||
|
freeNode(node->path.thenPath);
|
||||||
|
freeNode(node->path.elsePath);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEINCREMENT_PREFIX:
|
||||||
|
case AST_NODEINCREMENT_POSTFIX:
|
||||||
|
freeLiteral(node->increment.identifier);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEIMPORT:
|
||||||
|
case AST_NODEEXPORT:
|
||||||
|
freeLiteral(node->import.identifier);
|
||||||
|
freeLiteral(node->import.alias);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AST_NODEINDEX:
|
||||||
|
case AST_NODEDOT:
|
||||||
|
freeNode(node->index.first);
|
||||||
|
freeNode(node->index.second);
|
||||||
|
freeNode(node->index.third);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (freeSelf) {
|
||||||
|
FREE(ASTNode, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeNode(ASTNode* node) {
|
||||||
|
freeNodeCustom(node, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
|
||||||
|
//allocate a new node
|
||||||
|
*nodeHandle = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
(*nodeHandle)->type = AST_NODELITERAL;
|
||||||
|
(*nodeHandle)->atomic.literal = copyLiteral(literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
|
||||||
|
//allocate a new node
|
||||||
|
*nodeHandle = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
(*nodeHandle)->type = AST_NODEUNARY;
|
||||||
|
(*nodeHandle)->unary.opcode = opcode;
|
||||||
|
(*nodeHandle)->unary.child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEBINARY;
|
||||||
|
tmp->binary.opcode = opcode;
|
||||||
|
tmp->binary.left = *nodeHandle;
|
||||||
|
tmp->binary.right = rhs;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeGrouping(ASTNode** nodeHandle) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEGROUPING;
|
||||||
|
tmp->grouping.child = *nodeHandle;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeBlock(ASTNode** nodeHandle) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEBLOCK;
|
||||||
|
tmp->block.nodes = NULL;
|
||||||
|
tmp->block.capacity = 0;
|
||||||
|
tmp->block.count = 0;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODECOMPOUND;
|
||||||
|
tmp->compound.literalType = literalType;
|
||||||
|
tmp->compound.nodes = NULL;
|
||||||
|
tmp->compound.capacity = 0;
|
||||||
|
tmp->compound.count = 0;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right) {
|
||||||
|
//assume the node has already been allocated
|
||||||
|
node->type = AST_NODEPAIR;
|
||||||
|
node->pair.left = left;
|
||||||
|
node->pair.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEVAR_DECL;
|
||||||
|
tmp->varDecl.identifier = identifier;
|
||||||
|
tmp->varDecl.typeLiteral = typeLiteral;
|
||||||
|
tmp->varDecl.expression = expression;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEFN_DECL;
|
||||||
|
tmp->fnDecl.identifier = identifier;
|
||||||
|
tmp->fnDecl.arguments = arguments;
|
||||||
|
tmp->fnDecl.returns = returns;
|
||||||
|
tmp->fnDecl.block = block;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEFN_CALL;
|
||||||
|
tmp->fnCall.arguments = arguments;
|
||||||
|
tmp->fnCall.argumentCount = argumentCount;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEFN_COLLECTION;
|
||||||
|
tmp->fnCollection.nodes = NULL;
|
||||||
|
tmp->fnCollection.capacity = 0;
|
||||||
|
tmp->fnCollection.count = 0;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = type;
|
||||||
|
tmp->path.preClause = preClause;
|
||||||
|
tmp->path.postClause = postClause;
|
||||||
|
tmp->path.condition = condition;
|
||||||
|
tmp->path.thenPath = thenPath;
|
||||||
|
tmp->path.elsePath = elsePath;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEINCREMENT_PREFIX;
|
||||||
|
tmp->increment.identifier = copyLiteral(identifier);
|
||||||
|
tmp->increment.increment = increment;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEINCREMENT_POSTFIX;
|
||||||
|
tmp->increment.identifier = copyLiteral(identifier);
|
||||||
|
tmp->increment.increment = increment;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = mode;
|
||||||
|
tmp->import.identifier = copyLiteral(identifier);
|
||||||
|
tmp->import.alias = copyLiteral(alias);
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEINDEX;
|
||||||
|
tmp->index.first = first;
|
||||||
|
tmp->index.second = second;
|
||||||
|
tmp->index.third = third;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first) {
|
||||||
|
ASTNode* tmp = ALLOCATE(ASTNode, 1);
|
||||||
|
|
||||||
|
tmp->type = AST_NODEDOT;
|
||||||
|
tmp->index.first = first;
|
||||||
|
tmp->index.second = NULL;
|
||||||
|
tmp->index.third = NULL;
|
||||||
|
|
||||||
|
*nodeHandle = tmp;
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "literal.h"
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "token_types.h"
|
||||||
|
|
||||||
|
//nodes are the intermediaries between parsers and compilers
|
||||||
|
typedef union _node ASTNode;
|
||||||
|
|
||||||
|
typedef enum ASTNodeType {
|
||||||
|
AST_NODEERROR,
|
||||||
|
AST_NODELITERAL, //a simple value
|
||||||
|
AST_NODEUNARY, //one child + opcode
|
||||||
|
AST_NODEBINARY, //two children, left and right + opcode
|
||||||
|
AST_NODEGROUPING, //one child
|
||||||
|
AST_NODEBLOCK, //contains a sub-node array
|
||||||
|
AST_NODECOMPOUND, //contains a sub-node array
|
||||||
|
AST_NODEPAIR, //contains a left and right
|
||||||
|
AST_NODEVAR_DECL, //contains identifier literal, typenode, expression definition
|
||||||
|
AST_NODEFN_DECL, //containd identifier literal, arguments node, returns node, block node
|
||||||
|
AST_NODEFN_COLLECTION, //parts of a function
|
||||||
|
AST_NODEFN_CALL,
|
||||||
|
AST_NODEPATH_IF, //for control flow
|
||||||
|
AST_NODEPATH_WHILE, //for control flow
|
||||||
|
AST_NODEPATH_FOR, //for control flow
|
||||||
|
AST_NODEPATH_BREAK, //for control flow
|
||||||
|
AST_NODEPATH_CONTINUE, //for control flow
|
||||||
|
AST_NODEPATH_RETURN,
|
||||||
|
AST_NODEINCREMENT_PREFIX,
|
||||||
|
AST_NODEINCREMENT_POSTFIX,
|
||||||
|
AST_NODEIMPORT,
|
||||||
|
AST_NODEEXPORT,
|
||||||
|
AST_NODEINDEX,
|
||||||
|
AST_NODEDOT,
|
||||||
|
} ASTNodeType;
|
||||||
|
|
||||||
|
typedef struct NodeLiteral {
|
||||||
|
ASTNodeType type;
|
||||||
|
Literal literal;
|
||||||
|
} NodeLiteral;
|
||||||
|
|
||||||
|
typedef struct NodeUnary {
|
||||||
|
ASTNodeType type;
|
||||||
|
Opcode opcode;
|
||||||
|
ASTNode* child;
|
||||||
|
} NodeUnary;
|
||||||
|
|
||||||
|
typedef struct NodeBinary {
|
||||||
|
ASTNodeType type;
|
||||||
|
Opcode opcode;
|
||||||
|
ASTNode* left;
|
||||||
|
ASTNode* right;
|
||||||
|
} NodeBinary;
|
||||||
|
|
||||||
|
typedef struct NodeGrouping {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* child;
|
||||||
|
} NodeGrouping;
|
||||||
|
|
||||||
|
typedef struct NodeBlock {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* nodes;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} NodeBlock;
|
||||||
|
|
||||||
|
typedef struct NodeCompound {
|
||||||
|
ASTNodeType type;
|
||||||
|
LiteralType literalType;
|
||||||
|
ASTNode* nodes;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} NodeCompound;
|
||||||
|
|
||||||
|
typedef struct NodePair {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* left;
|
||||||
|
ASTNode* right;
|
||||||
|
} NodePair;
|
||||||
|
|
||||||
|
typedef struct NodeVarDecl {
|
||||||
|
ASTNodeType type;
|
||||||
|
Literal identifier;
|
||||||
|
Literal typeLiteral;
|
||||||
|
ASTNode* expression;
|
||||||
|
} NodeVarDecl;
|
||||||
|
|
||||||
|
typedef struct NodeFnDecl {
|
||||||
|
ASTNodeType type;
|
||||||
|
Literal identifier;
|
||||||
|
ASTNode* arguments;
|
||||||
|
ASTNode* returns;
|
||||||
|
ASTNode* block;
|
||||||
|
} NodeFnDecl;
|
||||||
|
|
||||||
|
typedef struct NodeFnCollection {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* nodes;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} NodeFnCollection;
|
||||||
|
|
||||||
|
typedef struct NodeFnCall {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* arguments;
|
||||||
|
int argumentCount;
|
||||||
|
} NodeFnCall;
|
||||||
|
|
||||||
|
typedef struct NodePath {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* preClause;
|
||||||
|
ASTNode* postClause;
|
||||||
|
ASTNode* condition;
|
||||||
|
ASTNode* thenPath;
|
||||||
|
ASTNode* elsePath;
|
||||||
|
} NodePath;
|
||||||
|
|
||||||
|
typedef struct NodeIncrement {
|
||||||
|
ASTNodeType type;
|
||||||
|
Literal identifier;
|
||||||
|
int increment;
|
||||||
|
} NodeIncrement;
|
||||||
|
|
||||||
|
typedef struct NodeImport {
|
||||||
|
ASTNodeType type;
|
||||||
|
Literal identifier;
|
||||||
|
Literal alias;
|
||||||
|
} NodeImport;
|
||||||
|
|
||||||
|
typedef struct NodeIndex {
|
||||||
|
ASTNodeType type;
|
||||||
|
ASTNode* first;
|
||||||
|
ASTNode* second;
|
||||||
|
ASTNode* third;
|
||||||
|
} NodeIndex;
|
||||||
|
|
||||||
|
union _node {
|
||||||
|
ASTNodeType type;
|
||||||
|
NodeLiteral atomic;
|
||||||
|
NodeUnary unary;
|
||||||
|
NodeBinary binary;
|
||||||
|
NodeGrouping grouping;
|
||||||
|
NodeBlock block;
|
||||||
|
NodeCompound compound;
|
||||||
|
NodePair pair;
|
||||||
|
NodeVarDecl varDecl;
|
||||||
|
NodeFnDecl fnDecl;
|
||||||
|
NodeFnCollection fnCollection;
|
||||||
|
NodeFnCall fnCall;
|
||||||
|
NodePath path;
|
||||||
|
NodeIncrement increment;
|
||||||
|
NodeImport import;
|
||||||
|
NodeIndex index;
|
||||||
|
};
|
||||||
|
|
||||||
|
TOY_API void freeNode(ASTNode* node);
|
||||||
|
|
||||||
|
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
|
||||||
|
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
|
||||||
|
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
|
||||||
|
void emitASTNodeGrouping(ASTNode** nodeHandle);
|
||||||
|
void emitASTNodeBlock(ASTNode** nodeHandle);
|
||||||
|
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
|
||||||
|
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right);
|
||||||
|
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
|
||||||
|
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
|
||||||
|
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount);
|
||||||
|
void emitASTNodeFnCollection(ASTNode** nodeHandle);
|
||||||
|
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
|
||||||
|
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
|
||||||
|
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
|
||||||
|
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias);
|
||||||
|
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
|
||||||
|
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
int _index(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
int _set(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
int _get(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
int _push(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
int _pop(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
int _length(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
int _clear(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "ast_node.h"
|
||||||
|
#include "literal_array.h"
|
||||||
|
|
||||||
|
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
||||||
|
typedef struct Compiler {
|
||||||
|
LiteralArray literalCache;
|
||||||
|
unsigned char* bytecode;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} Compiler;
|
||||||
|
|
||||||
|
TOY_API void initCompiler(Compiler* compiler);
|
||||||
|
TOY_API void writeCompiler(Compiler* compiler, ASTNode* node);
|
||||||
|
TOY_API void freeCompiler(Compiler* compiler);
|
||||||
|
|
||||||
|
//embed the header, data section, code section, function section, etc.
|
||||||
|
TOY_API unsigned char* collateCompiler(Compiler* compiler, int* size);
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
//NOTE: you need both font AND background for these to work
|
||||||
|
|
||||||
|
//fonts color
|
||||||
|
#define FONT_BLACK "\033[30;"
|
||||||
|
#define FONT_RED "\033[31;"
|
||||||
|
#define FONT_GREEN "\033[32;"
|
||||||
|
#define FONT_YELLOW "\033[33;"
|
||||||
|
#define FONT_BLUE "\033[34;"
|
||||||
|
#define FONT_PURPLE "\033[35;"
|
||||||
|
#define FONT_DGREEN "\033[6;"
|
||||||
|
#define FONT_WHITE "\033[7;"
|
||||||
|
#define FONT_CYAN "\x1b[36m"
|
||||||
|
|
||||||
|
//background color
|
||||||
|
#define BACK_BLACK "40m"
|
||||||
|
#define BACK_RED "41m"
|
||||||
|
#define BACK_GREEN "42m"
|
||||||
|
#define BACK_YELLOW "43m"
|
||||||
|
#define BACK_BLUE "44m"
|
||||||
|
#define BACK_PURPLE "45m"
|
||||||
|
#define BACK_DGREEN "46m"
|
||||||
|
#define BACK_WHITE "47m"
|
||||||
|
|
||||||
|
//useful
|
||||||
|
#define NOTICE FONT_GREEN BACK_BLACK
|
||||||
|
#define WARN FONT_YELLOW BACK_BLACK
|
||||||
|
#define ERROR FONT_RED BACK_BLACK
|
||||||
|
#define RESET "\033[0m"
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "literal.h"
|
||||||
|
#include "literal_array.h"
|
||||||
|
#include "literal_dictionary.h"
|
||||||
|
#include "scope.h"
|
||||||
|
|
||||||
|
typedef void (*PrintFn)(const char*);
|
||||||
|
|
||||||
|
//the interpreter acts depending on the bytecode instructions
|
||||||
|
typedef struct Interpreter {
|
||||||
|
//input
|
||||||
|
unsigned char* bytecode;
|
||||||
|
int length;
|
||||||
|
int count;
|
||||||
|
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
||||||
|
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||||
|
|
||||||
|
//operation
|
||||||
|
Scope* scope;
|
||||||
|
LiteralArray stack;
|
||||||
|
|
||||||
|
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
|
||||||
|
LiteralDictionary* exportTypes;
|
||||||
|
LiteralDictionary* hooks;
|
||||||
|
|
||||||
|
//debug outputs
|
||||||
|
PrintFn printOutput;
|
||||||
|
PrintFn assertOutput;
|
||||||
|
PrintFn errorOutput;
|
||||||
|
|
||||||
|
int depth; //don't overflow
|
||||||
|
bool panic;
|
||||||
|
} Interpreter;
|
||||||
|
|
||||||
|
//native API
|
||||||
|
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
|
||||||
|
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
|
||||||
|
|
||||||
|
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
|
||||||
|
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
|
||||||
|
|
||||||
|
TOY_API bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* arguments, LiteralArray* returns);
|
||||||
|
TOY_API bool callFn(Interpreter* interpreter, char* name, LiteralArray* arguments, LiteralArray* returns);
|
||||||
|
|
||||||
|
//utilities for the host program
|
||||||
|
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
|
||||||
|
TOY_API void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput);
|
||||||
|
TOY_API void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
|
||||||
|
TOY_API void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
|
||||||
|
|
||||||
|
//main access
|
||||||
|
TOY_API void initInterpreter(Interpreter* interpreter); //start of program
|
||||||
|
TOY_API void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
|
||||||
|
TOY_API void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||||
|
TOY_API void freeInterpreter(Interpreter* interpreter); //end of program
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#include "keyword_types.h"
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
KeywordType keywordTypes[] = {
|
||||||
|
//type keywords
|
||||||
|
{TOKEN_NULL, "null"},
|
||||||
|
{TOKEN_BOOLEAN, "bool"},
|
||||||
|
{TOKEN_INTEGER, "int"},
|
||||||
|
{TOKEN_FLOAT, "float"},
|
||||||
|
{TOKEN_STRING, "string"},
|
||||||
|
{TOKEN_FUNCTION, "fn"},
|
||||||
|
{TOKEN_OPAQUE, "opaque"},
|
||||||
|
{TOKEN_ANY, "any"},
|
||||||
|
|
||||||
|
//other keywords
|
||||||
|
{TOKEN_AS, "as"},
|
||||||
|
{TOKEN_ASSERT, "assert"},
|
||||||
|
{TOKEN_BREAK, "break"},
|
||||||
|
{TOKEN_CLASS, "class"},
|
||||||
|
{TOKEN_CONST, "const"},
|
||||||
|
{TOKEN_CONTINUE, "continue"},
|
||||||
|
{TOKEN_DO, "do"},
|
||||||
|
{TOKEN_ELSE, "else"},
|
||||||
|
{TOKEN_EXPORT, "export"},
|
||||||
|
{TOKEN_FOR, "for"},
|
||||||
|
{TOKEN_FOREACH, "foreach"},
|
||||||
|
{TOKEN_IF, "if"},
|
||||||
|
{TOKEN_IMPORT, "import"},
|
||||||
|
{TOKEN_IN, "in"},
|
||||||
|
{TOKEN_OF, "of"},
|
||||||
|
{TOKEN_PRINT, "print"},
|
||||||
|
{TOKEN_RETURN, "return"},
|
||||||
|
{TOKEN_TYPE, "type"},
|
||||||
|
{TOKEN_ASTYPE, "astype"},
|
||||||
|
{TOKEN_TYPEOF, "typeof"},
|
||||||
|
{TOKEN_VAR, "var"},
|
||||||
|
{TOKEN_WHILE, "while"},
|
||||||
|
|
||||||
|
//literal values
|
||||||
|
{TOKEN_LITERAL_TRUE, "true"},
|
||||||
|
{TOKEN_LITERAL_FALSE, "false"},
|
||||||
|
|
||||||
|
//meta tokens
|
||||||
|
{TOKEN_PASS, NULL},
|
||||||
|
{TOKEN_ERROR, NULL},
|
||||||
|
|
||||||
|
{TOKEN_EOF, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
char* findKeywordByType(TokenType type) {
|
||||||
|
if (type == TOKEN_EOF) {
|
||||||
|
return "EOF";
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; keywordTypes[i].keyword; i++) {
|
||||||
|
if (keywordTypes[i].type == type) {
|
||||||
|
return keywordTypes[i].keyword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType findTypeByKeyword(const char* keyword) {
|
||||||
|
const int length = strlen(keyword);
|
||||||
|
|
||||||
|
for (int i = 0; keywordTypes[i].keyword; i++) {
|
||||||
|
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
|
||||||
|
return keywordTypes[i].type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TOKEN_EOF;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "token_types.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TokenType type;
|
||||||
|
char* keyword;
|
||||||
|
} KeywordType;
|
||||||
|
|
||||||
|
extern KeywordType keywordTypes[];
|
||||||
|
|
||||||
|
char* findKeywordByType(TokenType type);
|
||||||
|
|
||||||
|
TokenType findTypeByKeyword(const char* keyword);
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
#include "lexer.h"
|
||||||
|
#include "console_colors.h"
|
||||||
|
#include "keyword_types.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
//static generic utility functions
|
||||||
|
static void cleanLexer(Lexer* lexer) {
|
||||||
|
lexer->source = NULL;
|
||||||
|
lexer->start = 0;
|
||||||
|
lexer->current = 0;
|
||||||
|
lexer->line = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isAtEnd(Lexer* lexer) {
|
||||||
|
return lexer->source[lexer->current] == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static char peek(Lexer* lexer) {
|
||||||
|
return lexer->source[lexer->current];
|
||||||
|
}
|
||||||
|
|
||||||
|
static char peekNext(Lexer* lexer) {
|
||||||
|
if (isAtEnd(lexer)) return '\0';
|
||||||
|
return lexer->source[lexer->current + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static char advance(Lexer* lexer) {
|
||||||
|
if (isAtEnd(lexer)) {
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
//new line
|
||||||
|
if (lexer->source[lexer->current] == '\n') {
|
||||||
|
lexer->line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer->current++;
|
||||||
|
return lexer->source[lexer->current - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void eatWhitespace(Lexer* lexer) {
|
||||||
|
const char c = peek(lexer);
|
||||||
|
|
||||||
|
switch(c) {
|
||||||
|
case ' ':
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
case '\t':
|
||||||
|
advance(lexer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//comments
|
||||||
|
case '/':
|
||||||
|
//eat the line
|
||||||
|
if (peekNext(lexer) == '/') {
|
||||||
|
while (advance(lexer) != '\n' && !isAtEnd(lexer));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//eat the block
|
||||||
|
if (peekNext(lexer) == '*') {
|
||||||
|
advance(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//tail recursion
|
||||||
|
eatWhitespace(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isDigit(Lexer* lexer) {
|
||||||
|
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isAlpha(Lexer* lexer) {
|
||||||
|
return
|
||||||
|
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
||||||
|
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
||||||
|
peek(lexer) == '_'
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool match(Lexer* lexer, char c) {
|
||||||
|
if (peek(lexer) == c) {
|
||||||
|
advance(lexer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//token generators
|
||||||
|
static Token makeErrorToken(Lexer* lexer, char* msg) {
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
token.type = TOKEN_ERROR;
|
||||||
|
token.lexeme = msg;
|
||||||
|
token.length = strlen(msg);
|
||||||
|
token.line = lexer->line;
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
if (command.verbose) {
|
||||||
|
printf("err:");
|
||||||
|
printToken(&token);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token makeToken(Lexer* lexer, TokenType type) {
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
token.type = type;
|
||||||
|
token.length = lexer->current - lexer->start;
|
||||||
|
token.lexeme = &lexer->source[lexer->current - token.length];
|
||||||
|
token.line = lexer->line;
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||||
|
if (command.verbose) {
|
||||||
|
printf("tok:");
|
||||||
|
printToken(&token);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token makeIntegerOrFloat(Lexer* lexer) {
|
||||||
|
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
|
||||||
|
|
||||||
|
while(isDigit(lexer)) advance(lexer);
|
||||||
|
|
||||||
|
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
||||||
|
type = TOKEN_LITERAL_FLOAT;
|
||||||
|
advance(lexer);
|
||||||
|
while(isDigit(lexer)) advance(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
token.type = type;
|
||||||
|
token.lexeme = &lexer->source[lexer->start];
|
||||||
|
token.length = lexer->current - lexer->start;
|
||||||
|
token.line = lexer->line;
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
if (command.verbose) {
|
||||||
|
if (type == TOKEN_LITERAL_INTEGER) {
|
||||||
|
printf("int:");
|
||||||
|
} else {
|
||||||
|
printf("flt:");
|
||||||
|
}
|
||||||
|
printToken(&token);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token makeString(Lexer* lexer, char terminator) {
|
||||||
|
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
||||||
|
advance(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(lexer); //eat terminator
|
||||||
|
|
||||||
|
if (isAtEnd(lexer)) {
|
||||||
|
return makeErrorToken(lexer, "Unterminated string");
|
||||||
|
}
|
||||||
|
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
token.type = TOKEN_LITERAL_STRING;
|
||||||
|
token.lexeme = &lexer->source[lexer->start + 1];
|
||||||
|
token.length = lexer->current - lexer->start - 2;
|
||||||
|
token.line = lexer->line;
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
if (command.verbose) {
|
||||||
|
printf("str:");
|
||||||
|
printToken(&token);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token makeKeywordOrIdentifier(Lexer* lexer) {
|
||||||
|
advance(lexer); //first letter can only be alpha
|
||||||
|
|
||||||
|
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||||
|
advance(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//scan for a keyword
|
||||||
|
for (int i = 0; keywordTypes[i].keyword; i++) {
|
||||||
|
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
token.type = keywordTypes[i].type;
|
||||||
|
token.lexeme = &lexer->source[lexer->start];
|
||||||
|
token.length = lexer->current - lexer->start;
|
||||||
|
token.line = lexer->line;
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
if (command.verbose) {
|
||||||
|
printf("kwd:");
|
||||||
|
printToken(&token);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//return an identifier
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
token.type = TOKEN_IDENTIFIER;
|
||||||
|
token.lexeme = &lexer->source[lexer->start];
|
||||||
|
token.length = lexer->current - lexer->start;
|
||||||
|
token.line = lexer->line;
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
if (command.verbose) {
|
||||||
|
printf("idf:");
|
||||||
|
printToken(&token);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
void initLexer(Lexer* lexer, char* source) {
|
||||||
|
cleanLexer(lexer);
|
||||||
|
|
||||||
|
lexer->source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token scanLexer(Lexer* lexer) {
|
||||||
|
eatWhitespace(lexer);
|
||||||
|
|
||||||
|
lexer->start = lexer->current;
|
||||||
|
|
||||||
|
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
|
||||||
|
|
||||||
|
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||||
|
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||||
|
|
||||||
|
char c = advance(lexer);
|
||||||
|
|
||||||
|
switch(c) {
|
||||||
|
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
|
||||||
|
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
|
||||||
|
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
|
||||||
|
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
|
||||||
|
case '[': return makeToken(lexer, TOKEN_BRACKET_LEFT);
|
||||||
|
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
|
||||||
|
|
||||||
|
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
|
||||||
|
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
|
||||||
|
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
|
||||||
|
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
|
||||||
|
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
|
||||||
|
|
||||||
|
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
|
||||||
|
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
|
||||||
|
|
||||||
|
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
|
||||||
|
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
|
||||||
|
|
||||||
|
case '&': //TOKEN_AND not used
|
||||||
|
if (advance(lexer) != '&') {
|
||||||
|
return makeErrorToken(lexer, "Unexpected '&'");
|
||||||
|
} else {
|
||||||
|
return makeToken(lexer, TOKEN_AND);
|
||||||
|
}
|
||||||
|
|
||||||
|
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
|
||||||
|
|
||||||
|
case ':': return makeToken(lexer, TOKEN_COLON);
|
||||||
|
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
||||||
|
case ',': return makeToken(lexer, TOKEN_COMMA);
|
||||||
|
case '.':
|
||||||
|
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
||||||
|
advance(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
return makeToken(lexer, TOKEN_REST);
|
||||||
|
}
|
||||||
|
return makeToken(lexer, TOKEN_DOT);
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
return makeString(lexer, c);
|
||||||
|
//TODO: possibly support interpolated strings
|
||||||
|
|
||||||
|
default: {
|
||||||
|
char buffer[128];
|
||||||
|
snprintf(buffer, 128, "Unexpected token: %c", c);
|
||||||
|
return makeErrorToken(lexer, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trim(char** s, int* l) { //all this to remove a newline?
|
||||||
|
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
||||||
|
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//for debugging
|
||||||
|
void printToken(Token* token) {
|
||||||
|
if (token->type == TOKEN_ERROR) {
|
||||||
|
printf(ERROR "Error\t%d\t%.*s\n" RESET, token->line, token->length, token->lexeme);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\t%d\t%d\t", token->type, token->line);
|
||||||
|
|
||||||
|
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
|
||||||
|
printf("%.*s\t", token->length, token->lexeme);
|
||||||
|
} else {
|
||||||
|
char* keyword = findKeywordByType(token->type);
|
||||||
|
|
||||||
|
if (keyword != NULL) {
|
||||||
|
printf("%s", keyword);
|
||||||
|
} else {
|
||||||
|
char* str = token->lexeme;
|
||||||
|
int length = token->length;
|
||||||
|
trim(&str, &length);
|
||||||
|
printf("%.*s", length, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "token_types.h"
|
||||||
|
|
||||||
|
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||||
|
typedef struct {
|
||||||
|
char* source;
|
||||||
|
int start; //start of the token
|
||||||
|
int current; //current position of the lexer
|
||||||
|
int line; //track this for error handling
|
||||||
|
} Lexer;
|
||||||
|
|
||||||
|
//tokens are intermediaries between lexers and parsers
|
||||||
|
typedef struct {
|
||||||
|
TokenType type;
|
||||||
|
char* lexeme;
|
||||||
|
int length;
|
||||||
|
int line;
|
||||||
|
} Token;
|
||||||
|
|
||||||
|
TOY_API void initLexer(Lexer* lexer, char* source);
|
||||||
|
Token scanLexer(Lexer* lexer);
|
||||||
|
|
||||||
|
//for debugging
|
||||||
|
void printToken(Token* token);
|
||||||
@@ -0,0 +1,703 @@
|
|||||||
|
#include "literal.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include "literal_array.h"
|
||||||
|
#include "literal_dictionary.h"
|
||||||
|
#include "scope.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//hash util functions
|
||||||
|
static unsigned int hashString(const char* string, int length) {
|
||||||
|
unsigned int hash = 2166136261u;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
hash *= string[i];
|
||||||
|
hash ^= 16777619;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int hash(unsigned int x) {
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||||
|
x = (x >> 16) ^ x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
void freeLiteral(Literal literal) {
|
||||||
|
if (IS_STRING(literal)) {
|
||||||
|
FREE_ARRAY(char, AS_STRING(literal), literal.as.string.length + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ARRAY(literal) || literal.type == LITERAL_DICTIONARY_INTERMEDIATE || literal.type == LITERAL_TYPE_INTERMEDIATE) {
|
||||||
|
freeLiteralArray(AS_ARRAY(literal));
|
||||||
|
FREE(LiteralArray, AS_ARRAY(literal));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_DICTIONARY(literal)) {
|
||||||
|
freeLiteralDictionary(AS_DICTIONARY(literal));
|
||||||
|
FREE(LiteralDictionary, AS_DICTIONARY(literal));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_FUNCTION(literal)) {
|
||||||
|
popScope(AS_FUNCTION(literal).scope);
|
||||||
|
AS_FUNCTION(literal).scope = NULL;
|
||||||
|
FREE_ARRAY(unsigned char, AS_FUNCTION(literal).bytecode, AS_FUNCTION(literal).length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_IDENTIFIER(literal)) {
|
||||||
|
FREE_ARRAY(char, AS_IDENTIFIER(literal), literal.as.identifier.length + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_TYPE(literal)) {
|
||||||
|
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
||||||
|
freeLiteral(((Literal*)(AS_TYPE(literal).subtypes))[i]);
|
||||||
|
}
|
||||||
|
FREE_ARRAY(Literal, AS_TYPE(literal).subtypes, AS_TYPE(literal).capacity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isTruthy(Literal x) {
|
||||||
|
if (IS_NULL(x)) {
|
||||||
|
fprintf(stderr, ERROR "ERROR: Null is neither true nor false\n" RESET);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_BOOLEAN(x)) {
|
||||||
|
return AS_BOOLEAN(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal _toStringLiteral(char* str, int length) {
|
||||||
|
return ((Literal){LITERAL_STRING, { .string.ptr = (char*)str, .string.length = length }});
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal _toIdentifierLiteral(char* str, int length) {
|
||||||
|
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = (char*)str, .identifier.length = length, .identifier.hash = hashString(str, length) }});
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal* _typePushSubtype(Literal* lit, Literal subtype) {
|
||||||
|
//grow the subtype array
|
||||||
|
if (AS_TYPE(*lit).count + 1 > AS_TYPE(*lit).capacity) {
|
||||||
|
int oldCapacity = AS_TYPE(*lit).capacity;
|
||||||
|
|
||||||
|
AS_TYPE(*lit).capacity = GROW_CAPACITY(oldCapacity);
|
||||||
|
AS_TYPE(*lit).subtypes = GROW_ARRAY(Literal, AS_TYPE(*lit).subtypes, oldCapacity, AS_TYPE(*lit).capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
//actually push
|
||||||
|
((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count++ ] = subtype;
|
||||||
|
return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal copyLiteral(Literal original) {
|
||||||
|
switch(original.type) {
|
||||||
|
case LITERAL_NULL:
|
||||||
|
case LITERAL_BOOLEAN:
|
||||||
|
case LITERAL_INTEGER:
|
||||||
|
case LITERAL_FLOAT:
|
||||||
|
//no copying needed
|
||||||
|
return original;
|
||||||
|
|
||||||
|
case LITERAL_STRING: {
|
||||||
|
return TO_STRING_LITERAL(copyString(AS_STRING(original), original.as.string.length), original.as.string.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_ARRAY: {
|
||||||
|
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||||
|
initLiteralArray(array);
|
||||||
|
|
||||||
|
//copy each element
|
||||||
|
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
||||||
|
pushLiteralArray(array, AS_ARRAY(original)->literals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TO_ARRAY_LITERAL(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_DICTIONARY: {
|
||||||
|
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
|
||||||
|
initLiteralDictionary(dictionary);
|
||||||
|
|
||||||
|
//copy each entry
|
||||||
|
for (int i = 0; i < AS_DICTIONARY(original)->capacity; i++) {
|
||||||
|
if ( !IS_NULL(AS_DICTIONARY(original)->entries[i].key) ) {
|
||||||
|
setLiteralDictionary(dictionary, AS_DICTIONARY(original)->entries[i].key, AS_DICTIONARY(original)->entries[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TO_DICTIONARY_LITERAL(dictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION: {
|
||||||
|
unsigned char* buffer = ALLOCATE(unsigned char, AS_FUNCTION(original).length);
|
||||||
|
memcpy(buffer, AS_FUNCTION(original).bytecode, AS_FUNCTION(original).length);
|
||||||
|
|
||||||
|
Literal literal = TO_FUNCTION_LITERAL(buffer, AS_FUNCTION(original).length);
|
||||||
|
AS_FUNCTION(literal).scope = copyScope(AS_FUNCTION(original).scope);
|
||||||
|
|
||||||
|
return literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_IDENTIFIER: {
|
||||||
|
return TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(original), original.as.identifier.length), original.as.identifier.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_TYPE: {
|
||||||
|
Literal lit = TO_TYPE_LITERAL(AS_TYPE(original).typeOf, AS_TYPE(original).constant);
|
||||||
|
|
||||||
|
for (int i = 0; i < AS_TYPE(original).count; i++) {
|
||||||
|
TYPE_PUSH_SUBTYPE(&lit, copyLiteral( ((Literal*)(AS_TYPE(original).subtypes))[i] ));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE: {
|
||||||
|
return original; //literally a shallow copy
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||||
|
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||||
|
initLiteralArray(array);
|
||||||
|
|
||||||
|
//copy each element
|
||||||
|
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
||||||
|
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
|
||||||
|
pushLiteralArray(array, literal);
|
||||||
|
freeLiteral(literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal ret = TO_ARRAY_LITERAL(array);
|
||||||
|
ret.type = LITERAL_DICTIONARY_INTERMEDIATE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_TYPE_INTERMEDIATE: {
|
||||||
|
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||||
|
initLiteralArray(array);
|
||||||
|
|
||||||
|
//copy each element
|
||||||
|
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
|
||||||
|
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
|
||||||
|
pushLiteralArray(array, literal);
|
||||||
|
freeLiteral(literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal ret = TO_ARRAY_LITERAL(array);
|
||||||
|
ret.type = LITERAL_TYPE_INTERMEDIATE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
||||||
|
case LITERAL_FUNCTION_NATIVE:
|
||||||
|
//no copying possible
|
||||||
|
return original;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, ERROR "ERROR: Can't copy that literal type: %d\n" RESET, original.type);
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* copyString(char* original, int length) {
|
||||||
|
//make a local copy of the char array
|
||||||
|
char* buffer = ALLOCATE(char, length + 1);
|
||||||
|
strncpy(buffer, original, length);
|
||||||
|
buffer[length] = '\0';
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool literalsAreEqual(Literal lhs, Literal rhs) {
|
||||||
|
//utility for other things
|
||||||
|
if (lhs.type != rhs.type) {
|
||||||
|
// ints and floats are compatible
|
||||||
|
if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) {
|
||||||
|
if (IS_INTEGER(lhs)) {
|
||||||
|
return AS_INTEGER(lhs) + AS_FLOAT(rhs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return AS_FLOAT(lhs) + AS_INTEGER(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(lhs.type) {
|
||||||
|
case LITERAL_NULL:
|
||||||
|
return true; //can only be true because of the check above
|
||||||
|
|
||||||
|
case LITERAL_BOOLEAN:
|
||||||
|
return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs);
|
||||||
|
|
||||||
|
case LITERAL_INTEGER:
|
||||||
|
return AS_INTEGER(lhs) == AS_INTEGER(rhs);
|
||||||
|
|
||||||
|
case LITERAL_FLOAT:
|
||||||
|
return AS_FLOAT(lhs) == AS_FLOAT(rhs);
|
||||||
|
|
||||||
|
case LITERAL_STRING:
|
||||||
|
if (lhs.as.string.length != rhs.as.string.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !strncmp(AS_STRING(lhs), AS_STRING(rhs), lhs.as.string.length);
|
||||||
|
|
||||||
|
case LITERAL_ARRAY:
|
||||||
|
case LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
||||||
|
case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
||||||
|
//mismatched sizes
|
||||||
|
if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//mismatched elements (in order)
|
||||||
|
for (int i = 0; i < AS_ARRAY(lhs)->count; i++) {
|
||||||
|
if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case LITERAL_DICTIONARY:
|
||||||
|
//relatively slow, especially when nested
|
||||||
|
for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) {
|
||||||
|
if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
||||||
|
//check it exists in rhs
|
||||||
|
if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//compare the values
|
||||||
|
Literal val = getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
|
||||||
|
if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, val)) {
|
||||||
|
freeLiteral(val);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
freeLiteral(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION:
|
||||||
|
case LITERAL_FUNCTION_NATIVE:
|
||||||
|
return false; //functions are never equal
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_IDENTIFIER:
|
||||||
|
//check shortcuts
|
||||||
|
if (HASH_I(lhs) != HASH_I(rhs) && lhs.as.identifier.length != rhs.as.identifier.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), lhs.as.identifier.length);
|
||||||
|
|
||||||
|
case LITERAL_TYPE:
|
||||||
|
//check types
|
||||||
|
if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//const don't match
|
||||||
|
if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check subtypes
|
||||||
|
if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check array|dictionary signatures are the same (in order)
|
||||||
|
if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) {
|
||||||
|
for (int i = 0; i < AS_TYPE(lhs).count; i++) {
|
||||||
|
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
return false; //IDK what this is!
|
||||||
|
|
||||||
|
case LITERAL_ANY:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION_INTERMEDIATE:
|
||||||
|
fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//should never be seen
|
||||||
|
fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hashLiteral(Literal lit) {
|
||||||
|
switch(lit.type) {
|
||||||
|
case LITERAL_NULL:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case LITERAL_BOOLEAN:
|
||||||
|
return AS_BOOLEAN(lit) ? 1 : 0;
|
||||||
|
|
||||||
|
case LITERAL_INTEGER:
|
||||||
|
return hash((unsigned int)AS_INTEGER(lit));
|
||||||
|
|
||||||
|
case LITERAL_FLOAT:
|
||||||
|
return hash(*(unsigned int*)(&AS_FLOAT(lit)));
|
||||||
|
|
||||||
|
case LITERAL_STRING:
|
||||||
|
return hashString(AS_STRING(lit), strlen(AS_STRING(lit)));
|
||||||
|
|
||||||
|
case LITERAL_ARRAY: {
|
||||||
|
unsigned int res = 0;
|
||||||
|
for (int i = 0; i < AS_ARRAY(lit)->count; i++) {
|
||||||
|
res += hashLiteral(AS_ARRAY(lit)->literals[i]);
|
||||||
|
}
|
||||||
|
return hash(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_DICTIONARY: {
|
||||||
|
unsigned int res = 0;
|
||||||
|
for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) {
|
||||||
|
if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
||||||
|
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key);
|
||||||
|
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION:
|
||||||
|
case LITERAL_FUNCTION_NATIVE:
|
||||||
|
return 0; //TODO: find a way to hash these properly
|
||||||
|
|
||||||
|
case LITERAL_IDENTIFIER:
|
||||||
|
return HASH_I(lit); //pre-computed
|
||||||
|
|
||||||
|
case LITERAL_TYPE:
|
||||||
|
return AS_TYPE(lit).typeOf; //nothing else I can do
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
case LITERAL_ANY:
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//should never bee seen
|
||||||
|
fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//utils
|
||||||
|
static void stdoutWrapper(const char* output) {
|
||||||
|
printf("%s", output);
|
||||||
|
}
|
||||||
|
|
||||||
|
//buffer the prints
|
||||||
|
static char* globalPrintBuffer = NULL;
|
||||||
|
static size_t globalPrintCapacity = 0;
|
||||||
|
static size_t globalPrintCount = 0;
|
||||||
|
|
||||||
|
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
||||||
|
static char quotes = 0; //set to 0 to not show string quotes
|
||||||
|
|
||||||
|
static void printToBuffer(const char* str) {
|
||||||
|
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
|
||||||
|
int oldCapacity = globalPrintCapacity;
|
||||||
|
|
||||||
|
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
|
||||||
|
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
|
||||||
|
globalPrintCount += strlen(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
void printLiteral(Literal literal) {
|
||||||
|
printLiteralCustom(literal, stdoutWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
||||||
|
switch(literal.type) {
|
||||||
|
case LITERAL_NULL:
|
||||||
|
printFn("null");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_BOOLEAN:
|
||||||
|
printFn(AS_BOOLEAN(literal) ? "true" : "false");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_INTEGER: {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
|
||||||
|
printFn(buffer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_FLOAT: {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
|
||||||
|
printFn(buffer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_STRING: {
|
||||||
|
char buffer[MAX_STRING_LENGTH];
|
||||||
|
if (!quotes) {
|
||||||
|
snprintf(buffer, MAX_STRING_LENGTH, "%.*s", (int)strlen(AS_STRING(literal)), AS_STRING(literal));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)strlen(AS_STRING(literal)), AS_STRING(literal), quotes);
|
||||||
|
}
|
||||||
|
printFn(buffer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_ARRAY: {
|
||||||
|
LiteralArray* ptr = AS_ARRAY(literal);
|
||||||
|
|
||||||
|
//hold potential parent-call buffers on the C stack
|
||||||
|
char* cacheBuffer = globalPrintBuffer;
|
||||||
|
globalPrintBuffer = NULL;
|
||||||
|
int cacheCapacity = globalPrintCapacity;
|
||||||
|
globalPrintCapacity = 0;
|
||||||
|
int cacheCount = globalPrintCount;
|
||||||
|
globalPrintCount = 0;
|
||||||
|
|
||||||
|
//print the contents to the global buffer
|
||||||
|
printToBuffer("[");
|
||||||
|
for (int i = 0; i < ptr->count; i++) {
|
||||||
|
quotes = '"';
|
||||||
|
printLiteralCustom(ptr->literals[i], printToBuffer);
|
||||||
|
|
||||||
|
if (i + 1 < ptr->count) {
|
||||||
|
printToBuffer(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printToBuffer("]");
|
||||||
|
|
||||||
|
//swap the parent-call buffer back into place
|
||||||
|
char* printBuffer = globalPrintBuffer;
|
||||||
|
int printCapacity = globalPrintCapacity;
|
||||||
|
int printCount = globalPrintCount;
|
||||||
|
|
||||||
|
globalPrintBuffer = cacheBuffer;
|
||||||
|
globalPrintCapacity = cacheCapacity;
|
||||||
|
globalPrintCount = cacheCount;
|
||||||
|
|
||||||
|
//finally, output and cleanup
|
||||||
|
printFn(printBuffer);
|
||||||
|
FREE_ARRAY(char, printBuffer, printCapacity);
|
||||||
|
quotes = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_DICTIONARY: {
|
||||||
|
LiteralDictionary* ptr = AS_DICTIONARY(literal);
|
||||||
|
|
||||||
|
//hold potential parent-call buffers on the C stack
|
||||||
|
char* cacheBuffer = globalPrintBuffer;
|
||||||
|
globalPrintBuffer = NULL;
|
||||||
|
int cacheCapacity = globalPrintCapacity;
|
||||||
|
globalPrintCapacity = 0;
|
||||||
|
int cacheCount = globalPrintCount;
|
||||||
|
globalPrintCount = 0;
|
||||||
|
|
||||||
|
//print the contents to the global buffer
|
||||||
|
int delimCount = 0;
|
||||||
|
printToBuffer("[");
|
||||||
|
for (int i = 0; i < ptr->capacity; i++) {
|
||||||
|
if (IS_NULL(ptr->entries[i].key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delimCount++ > 0) {
|
||||||
|
printToBuffer(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
quotes = '"';
|
||||||
|
printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
||||||
|
printToBuffer(":");
|
||||||
|
quotes = '"';
|
||||||
|
printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//empty dicts MUST have a ":" printed
|
||||||
|
if (ptr->count == 0) {
|
||||||
|
printToBuffer(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
printToBuffer("]");
|
||||||
|
|
||||||
|
//swap the parent-call buffer back into place
|
||||||
|
char* printBuffer = globalPrintBuffer;
|
||||||
|
int printCapacity = globalPrintCapacity;
|
||||||
|
int printCount = globalPrintCount;
|
||||||
|
|
||||||
|
globalPrintBuffer = cacheBuffer;
|
||||||
|
globalPrintCapacity = cacheCapacity;
|
||||||
|
globalPrintCount = cacheCount;
|
||||||
|
|
||||||
|
//finally, output and cleanup
|
||||||
|
printFn(printBuffer);
|
||||||
|
FREE_ARRAY(char, printBuffer, printCapacity);
|
||||||
|
quotes = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
//TODO: functions
|
||||||
|
case LITERAL_FUNCTION:
|
||||||
|
case LITERAL_FUNCTION_NATIVE:
|
||||||
|
printFn("(function)");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_IDENTIFIER: {
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, 256, "%.*s", (int)strlen( AS_IDENTIFIER(literal) ), AS_IDENTIFIER(literal));
|
||||||
|
printFn(buffer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_TYPE: {
|
||||||
|
//hold potential parent-call buffers on the C stack
|
||||||
|
char* cacheBuffer = globalPrintBuffer;
|
||||||
|
globalPrintBuffer = NULL;
|
||||||
|
int cacheCapacity = globalPrintCapacity;
|
||||||
|
globalPrintCapacity = 0;
|
||||||
|
int cacheCount = globalPrintCount;
|
||||||
|
globalPrintCount = 0;
|
||||||
|
|
||||||
|
//print the type correctly
|
||||||
|
printToBuffer("<");
|
||||||
|
|
||||||
|
switch(AS_TYPE(literal).typeOf) {
|
||||||
|
case LITERAL_NULL:
|
||||||
|
printToBuffer("null");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_BOOLEAN:
|
||||||
|
printToBuffer("bool");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_INTEGER:
|
||||||
|
printToBuffer("int");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_FLOAT:
|
||||||
|
printToBuffer("float");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_STRING:
|
||||||
|
printToBuffer("string");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_ARRAY:
|
||||||
|
//print all in the array
|
||||||
|
printToBuffer("[");
|
||||||
|
for (int i = 0; i < AS_TYPE(literal).count; i++) {
|
||||||
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||||
|
}
|
||||||
|
printToBuffer("]");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_DICTIONARY:
|
||||||
|
printToBuffer("[");
|
||||||
|
|
||||||
|
for (int i = 0; i < AS_TYPE(literal).count; i += 2) {
|
||||||
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||||
|
printToBuffer(":");
|
||||||
|
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
|
||||||
|
}
|
||||||
|
printToBuffer("]");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION:
|
||||||
|
printToBuffer("function");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_FUNCTION_NATIVE:
|
||||||
|
printToBuffer("native");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_IDENTIFIER:
|
||||||
|
printToBuffer("identifier");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_TYPE:
|
||||||
|
printToBuffer("type");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
printToBuffer("opaque");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_ANY:
|
||||||
|
printToBuffer("any");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//should never be seen
|
||||||
|
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//const (printed last)
|
||||||
|
if (AS_TYPE(literal).constant) {
|
||||||
|
printToBuffer(" const");
|
||||||
|
}
|
||||||
|
|
||||||
|
printToBuffer(">");
|
||||||
|
|
||||||
|
//swap the parent-call buffer back into place
|
||||||
|
char* printBuffer = globalPrintBuffer;
|
||||||
|
int printCapacity = globalPrintCapacity;
|
||||||
|
int printCount = globalPrintCount;
|
||||||
|
|
||||||
|
globalPrintBuffer = cacheBuffer;
|
||||||
|
globalPrintCapacity = cacheCapacity;
|
||||||
|
globalPrintCount = cacheCount;
|
||||||
|
|
||||||
|
//finally, output and cleanup
|
||||||
|
printFn(printBuffer);
|
||||||
|
FREE_ARRAY(char, printBuffer, printCapacity);
|
||||||
|
quotes = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_TYPE_INTERMEDIATE:
|
||||||
|
case LITERAL_FUNCTION_INTERMEDIATE:
|
||||||
|
printFn("Unprintable literal found");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
printFn("(opaque)");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LITERAL_ANY:
|
||||||
|
printFn("(any)");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//should never be seen
|
||||||
|
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LITERAL_NULL,
|
||||||
|
LITERAL_BOOLEAN,
|
||||||
|
LITERAL_INTEGER,
|
||||||
|
LITERAL_FLOAT,
|
||||||
|
LITERAL_STRING,
|
||||||
|
LITERAL_ARRAY,
|
||||||
|
LITERAL_DICTIONARY,
|
||||||
|
LITERAL_FUNCTION,
|
||||||
|
LITERAL_IDENTIFIER,
|
||||||
|
LITERAL_TYPE,
|
||||||
|
LITERAL_OPAQUE,
|
||||||
|
LITERAL_ANY,
|
||||||
|
|
||||||
|
//these are meta-level types - not for general use
|
||||||
|
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||||
|
LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
||||||
|
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||||
|
LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
||||||
|
LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
||||||
|
} LiteralType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LiteralType type;
|
||||||
|
union {
|
||||||
|
bool boolean;
|
||||||
|
int integer;
|
||||||
|
float number;
|
||||||
|
struct {
|
||||||
|
char* ptr;
|
||||||
|
int length;
|
||||||
|
} string;
|
||||||
|
|
||||||
|
void* array;
|
||||||
|
void* dictionary;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
void* bytecode;
|
||||||
|
void* scope;
|
||||||
|
int length;
|
||||||
|
} function;
|
||||||
|
|
||||||
|
struct { //for variable names
|
||||||
|
char* ptr;
|
||||||
|
int length;
|
||||||
|
int hash;
|
||||||
|
} identifier;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
LiteralType typeOf; //no longer a mask
|
||||||
|
bool constant;
|
||||||
|
void* subtypes; //for nested types caused by compounds
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} type;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
void* ptr;
|
||||||
|
int tag;
|
||||||
|
} opaque;
|
||||||
|
} as;
|
||||||
|
} Literal;
|
||||||
|
|
||||||
|
#define IS_NULL(value) ((value).type == LITERAL_NULL)
|
||||||
|
#define IS_BOOLEAN(value) ((value).type == LITERAL_BOOLEAN)
|
||||||
|
#define IS_INTEGER(value) ((value).type == LITERAL_INTEGER)
|
||||||
|
#define IS_FLOAT(value) ((value).type == LITERAL_FLOAT)
|
||||||
|
#define IS_STRING(value) ((value).type == LITERAL_STRING)
|
||||||
|
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
|
||||||
|
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
|
||||||
|
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
|
||||||
|
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
|
||||||
|
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
|
||||||
|
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
|
||||||
|
#define IS_OPAQUE(value) ((value).type == LITERAL_OPAQUE)
|
||||||
|
|
||||||
|
#define AS_BOOLEAN(value) ((value).as.boolean)
|
||||||
|
#define AS_INTEGER(value) ((value).as.integer)
|
||||||
|
#define AS_FLOAT(value) ((value).as.number)
|
||||||
|
#define AS_STRING(value) ((value).as.string.ptr)
|
||||||
|
#define AS_ARRAY(value) ((LiteralArray*)((value).as.array))
|
||||||
|
#define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary))
|
||||||
|
#define AS_FUNCTION(value) ((value).as.function)
|
||||||
|
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||||
|
#define AS_TYPE(value) ((value).as.type)
|
||||||
|
#define AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||||
|
|
||||||
|
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
|
||||||
|
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
|
||||||
|
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
|
||||||
|
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
|
||||||
|
#define TO_STRING_LITERAL(value, l) _toStringLiteral(value, l)
|
||||||
|
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
|
||||||
|
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
|
||||||
|
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
||||||
|
#define TO_IDENTIFIER_LITERAL(value, l) _toIdentifierLiteral(value, l)
|
||||||
|
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
||||||
|
#define TO_OPAQUE_LITERAL(value, t) ((Literal){ LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
|
||||||
|
|
||||||
|
TOY_API void freeLiteral(Literal literal);
|
||||||
|
|
||||||
|
#define IS_TRUTHY(x) _isTruthy(x)
|
||||||
|
|
||||||
|
#define MAX_STRING_LENGTH 4096
|
||||||
|
#define HASH_I(lit) ((lit).as.identifier.hash)
|
||||||
|
#define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype)
|
||||||
|
#define OPAQUE_TAG(o) o.as.opaque.tag
|
||||||
|
|
||||||
|
//BUGFIX: macros are not functions
|
||||||
|
TOY_API bool _isTruthy(Literal x);
|
||||||
|
TOY_API Literal _toStringLiteral(char* str, int length);
|
||||||
|
TOY_API Literal _toIdentifierLiteral(char* str, int length);
|
||||||
|
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
|
||||||
|
|
||||||
|
//utils
|
||||||
|
TOY_API Literal copyLiteral(Literal original);
|
||||||
|
TOY_API char* copyString(char* original, int length);
|
||||||
|
TOY_API bool literalsAreEqual(Literal lhs, Literal rhs);
|
||||||
|
TOY_API int hashLiteral(Literal lit);
|
||||||
|
|
||||||
|
TOY_API void printLiteral(Literal literal);
|
||||||
|
TOY_API void printLiteralCustom(Literal literal, void (printFn)(const char*));
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
#include "literal_array.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
void initLiteralArray(LiteralArray* array) {
|
||||||
|
array->capacity = 0;
|
||||||
|
array->count = 0;
|
||||||
|
array->literals = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeLiteralArray(LiteralArray* array) {
|
||||||
|
//clean up memory
|
||||||
|
for(int i = 0; i < array->count; i++) {
|
||||||
|
freeLiteral(array->literals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FREE_ARRAY(Literal, array->literals, array->capacity);
|
||||||
|
initLiteralArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pushLiteralArray(LiteralArray* array, Literal literal) {
|
||||||
|
if (array->capacity < array->count + 1) {
|
||||||
|
int oldCapacity = array->capacity;
|
||||||
|
|
||||||
|
array->capacity = GROW_CAPACITY(oldCapacity);
|
||||||
|
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
array->literals[array->count] = copyLiteral(literal);
|
||||||
|
return array->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal popLiteralArray(LiteralArray* array) {
|
||||||
|
if (array->count <= 0) {
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the return
|
||||||
|
Literal ret = array->literals[array->count-1];
|
||||||
|
|
||||||
|
//null the existing data
|
||||||
|
array->literals[array->count-1] = TO_NULL_LITERAL;
|
||||||
|
|
||||||
|
array->count--;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//find a literal in the array that matches the "literal" argument
|
||||||
|
int findLiteralIndex(LiteralArray* array, Literal literal) {
|
||||||
|
for (int i = 0; i < array->count; i++) {
|
||||||
|
//not the same type
|
||||||
|
if (array->literals[i].type != literal.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//types match?
|
||||||
|
if (literalsAreEqual(array->literals[i], literal)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
|
||||||
|
if (!IS_INTEGER(index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = AS_INTEGER(index);
|
||||||
|
|
||||||
|
if (idx < 0 || idx >= array->count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: implicit push when referencing one-past-the-end?
|
||||||
|
|
||||||
|
freeLiteral(array->literals[idx]);
|
||||||
|
array->literals[idx] = copyLiteral(value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal getLiteralArray(LiteralArray* array, Literal index) {
|
||||||
|
if (!IS_INTEGER(index)) {
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = AS_INTEGER(index);
|
||||||
|
|
||||||
|
if (idx < 0 || idx >= array->count) {
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyLiteral(array->literals[idx]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "literal.h"
|
||||||
|
|
||||||
|
typedef struct LiteralArray {
|
||||||
|
Literal* literals;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
} LiteralArray;
|
||||||
|
|
||||||
|
TOY_API void initLiteralArray(LiteralArray* array);
|
||||||
|
TOY_API void freeLiteralArray(LiteralArray* array);
|
||||||
|
TOY_API int pushLiteralArray(LiteralArray* array, Literal literal);
|
||||||
|
TOY_API Literal popLiteralArray(LiteralArray* array);
|
||||||
|
TOY_API bool setLiteralArray(LiteralArray* array, Literal index, Literal value);
|
||||||
|
TOY_API Literal getLiteralArray(LiteralArray* array, Literal index);
|
||||||
|
|
||||||
|
int findLiteralIndex(LiteralArray* array, Literal literal);
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
#include "literal_dictionary.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//util functions
|
||||||
|
static void setEntryValues(_entry* entry, Literal key, Literal value) {
|
||||||
|
//much simpler now
|
||||||
|
freeLiteral(entry->key);
|
||||||
|
entry->key = copyLiteral(key);
|
||||||
|
|
||||||
|
freeLiteral(entry->value);
|
||||||
|
entry->value = copyLiteral(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _entry* getEntryArray(_entry* array, int capacity, Literal key, unsigned int hash, bool mustExist) {
|
||||||
|
//find "key", starting at index
|
||||||
|
unsigned int index = hash % capacity;
|
||||||
|
unsigned int start = index;
|
||||||
|
|
||||||
|
//increment once, so it can't equal start
|
||||||
|
index = (index + 1) % capacity;
|
||||||
|
|
||||||
|
//literal probing and collision checking
|
||||||
|
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
||||||
|
_entry* entry = &array[index];
|
||||||
|
|
||||||
|
if (IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
||||||
|
if (IS_NULL(entry->value) && !mustExist) {
|
||||||
|
//found a truly empty bucket
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
//else it's a tombstone - ignore
|
||||||
|
} else {
|
||||||
|
if (literalsAreEqual(key, entry->key)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = (index + 1) % capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
||||||
|
//new entry space
|
||||||
|
_entry* newEntries = ALLOCATE(_entry, capacity);
|
||||||
|
|
||||||
|
for (int i = 0; i < capacity; i++) {
|
||||||
|
newEntries[i].key = TO_NULL_LITERAL;
|
||||||
|
newEntries[i].value = TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//move the old array into the new one
|
||||||
|
for (int i = 0; i < oldCapacity; i++) {
|
||||||
|
if (IS_NULL((*dictionaryHandle)[i].key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//place the key and value in the new array (reusing string memory)
|
||||||
|
_entry* entry = getEntryArray(newEntries, capacity, TO_NULL_LITERAL, hashLiteral((*dictionaryHandle)[i].key), false);
|
||||||
|
|
||||||
|
entry->key = (*dictionaryHandle)[i].key;
|
||||||
|
entry->value = (*dictionaryHandle)[i].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear the old array
|
||||||
|
FREE_ARRAY(_entry, *dictionaryHandle, oldCapacity);
|
||||||
|
|
||||||
|
*dictionaryHandle = newEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
|
||||||
|
//expand array if needed
|
||||||
|
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
|
||||||
|
int oldCapacity = *capacityPtr;
|
||||||
|
*capacityPtr = GROW_CAPACITY(*capacityPtr);
|
||||||
|
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||||
|
}
|
||||||
|
|
||||||
|
_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
||||||
|
|
||||||
|
//true = contains increase
|
||||||
|
if (IS_NULL(entry->key)) {
|
||||||
|
setEntryValues(entry, key, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setEntryValues(entry, key, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeEntry(_entry* entry) {
|
||||||
|
freeLiteral(entry->key);
|
||||||
|
freeLiteral(entry->value);
|
||||||
|
entry->key = TO_NULL_LITERAL;
|
||||||
|
entry->value = TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeEntryArray(_entry* array, int capacity) {
|
||||||
|
if (array == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < capacity; i++) {
|
||||||
|
if (!IS_NULL(array[i].key)) {
|
||||||
|
freeEntry(&array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FREE_ARRAY(_entry, array, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
void initLiteralDictionary(LiteralDictionary* dictionary) {
|
||||||
|
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
||||||
|
dictionary->entries = NULL;
|
||||||
|
dictionary->capacity = GROW_CAPACITY(0);
|
||||||
|
dictionary->contains = 0;
|
||||||
|
dictionary->count = 0;
|
||||||
|
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeLiteralDictionary(LiteralDictionary* dictionary) {
|
||||||
|
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||||
|
dictionary->capacity = 0;
|
||||||
|
dictionary->contains = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
|
||||||
|
if (IS_NULL(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//BUGFIX: Can't hash a function
|
||||||
|
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
|
||||||
|
|
||||||
|
if (increment) {
|
||||||
|
dictionary->contains++;
|
||||||
|
dictionary->count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||||
|
if (IS_NULL(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//BUGFIX: Can't hash a function
|
||||||
|
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
||||||
|
|
||||||
|
if (entry != NULL) {
|
||||||
|
return copyLiteral(entry->value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||||
|
if (IS_NULL(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have null keys (remove)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//BUGFIX: Can't hash a function
|
||||||
|
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have function keys (remove)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
||||||
|
|
||||||
|
if (entry != NULL) {
|
||||||
|
freeEntry(entry);
|
||||||
|
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
|
||||||
|
dictionary->count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||||
|
//null & not tombstoned
|
||||||
|
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
|
||||||
|
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include "literal.h"
|
||||||
|
|
||||||
|
//TODO: benchmark this
|
||||||
|
#define DICTIONARY_MAX_LOAD 0.75
|
||||||
|
|
||||||
|
typedef struct _entry {
|
||||||
|
Literal key;
|
||||||
|
Literal value;
|
||||||
|
} _entry;
|
||||||
|
|
||||||
|
typedef struct LiteralDictionary {
|
||||||
|
_entry* entries;
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
int contains; //count + tombstones, for internal use
|
||||||
|
} LiteralDictionary;
|
||||||
|
|
||||||
|
TOY_API void initLiteralDictionary(LiteralDictionary* dictionary);
|
||||||
|
TOY_API void freeLiteralDictionary(LiteralDictionary* dictionary);
|
||||||
|
|
||||||
|
TOY_API void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
|
||||||
|
TOY_API Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
||||||
|
TOY_API void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
||||||
|
|
||||||
|
TOY_API bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
CC=gcc
|
||||||
|
|
||||||
|
IDIR+=.
|
||||||
|
CFLAGS+=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||||
|
LIBS+=
|
||||||
|
|
||||||
|
ODIR = obj
|
||||||
|
SRC = $(wildcard *.c)
|
||||||
|
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||||
|
|
||||||
|
OUTNAME=toy
|
||||||
|
|
||||||
|
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
||||||
|
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||||
|
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||||
|
else ifeq ($(shell uname),Linux)
|
||||||
|
LIBLINE=-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).a -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||||
|
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).so
|
||||||
|
CFLAGS += -fPIC
|
||||||
|
else ifeq ($(OS),Windows_NT)
|
||||||
|
LIBLINE =-Wl,--out-implib=../$(TOY_OUTDIR)/lib$(OUTNAME).dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive $(OBJ) -Wl,--no-whole-archive
|
||||||
|
OUT=../$(TOY_OUTDIR)/$(OUTNAME).dll
|
||||||
|
else ifeq ($(shell uname),Darwin)
|
||||||
|
LIBLINE = $(OBJ)
|
||||||
|
OUT=../$(TOY_OUTDIR)/lib$(OUTNAME).dylib
|
||||||
|
else
|
||||||
|
@echo "Platform test failed - what platform is this?"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
library: $(OBJ)
|
||||||
|
$(CC) -DTOY_EXPORT $(CFLAGS) -shared -o $(OUT) $(LIBLINE)
|
||||||
|
|
||||||
|
static: $(OBJ)
|
||||||
|
ar crs ../$(TOY_OUTDIR)/lib$(OUTNAME).a $(OBJ)
|
||||||
|
|
||||||
|
$(OBJ): | $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR):
|
||||||
|
mkdir $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR)/%.o: %.c
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(ODIR)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||||
|
if (newSize == 0 && oldSize == 0) {
|
||||||
|
//causes issues, so just skip out with a NO-OP
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newSize == 0) {
|
||||||
|
free(pointer);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* mem = realloc(pointer, newSize);
|
||||||
|
|
||||||
|
if (mem == NULL) {
|
||||||
|
fprintf(stderr, ERROR "[internal]Memory allocation error (requested %d for %ld, replacing %d)\n" ERROR, (int)newSize, (long int)pointer, (int)oldSize);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
|
||||||
|
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
|
||||||
|
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||||
|
#define GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
||||||
|
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||||
|
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||||
|
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||||
|
|
||||||
|
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum Opcode {
|
||||||
|
OP_EOF,
|
||||||
|
|
||||||
|
//basic statements
|
||||||
|
OP_ASSERT,
|
||||||
|
OP_PRINT,
|
||||||
|
|
||||||
|
//data
|
||||||
|
OP_LITERAL,
|
||||||
|
OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
||||||
|
OP_LITERAL_RAW, //forcibly get the raw value of the literal
|
||||||
|
|
||||||
|
//arithmetic operators
|
||||||
|
OP_NEGATE,
|
||||||
|
OP_ADDITION,
|
||||||
|
OP_SUBTRACTION,
|
||||||
|
OP_MULTIPLICATION,
|
||||||
|
OP_DIVISION,
|
||||||
|
OP_MODULO,
|
||||||
|
OP_GROUPING_BEGIN,
|
||||||
|
OP_GROUPING_END,
|
||||||
|
|
||||||
|
//variable stuff
|
||||||
|
OP_SCOPE_BEGIN,
|
||||||
|
OP_SCOPE_END,
|
||||||
|
|
||||||
|
OP_TYPE_DECL, //declare a type to be used (as a literal)
|
||||||
|
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
||||||
|
|
||||||
|
OP_VAR_DECL, //declare a variable to be used (as a literal)
|
||||||
|
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
||||||
|
|
||||||
|
OP_FN_DECL, //declare a function to be used (as a literal)
|
||||||
|
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
|
||||||
|
|
||||||
|
OP_VAR_ASSIGN, //assign to a literal
|
||||||
|
OP_VAR_ADDITION_ASSIGN,
|
||||||
|
OP_VAR_SUBTRACTION_ASSIGN,
|
||||||
|
OP_VAR_MULTIPLICATION_ASSIGN,
|
||||||
|
OP_VAR_DIVISION_ASSIGN,
|
||||||
|
OP_VAR_MODULO_ASSIGN,
|
||||||
|
|
||||||
|
OP_TYPE_CAST, //temporarily change a type of an atomic value
|
||||||
|
OP_TYPE_OF, //get the type of a variable
|
||||||
|
|
||||||
|
OP_IMPORT,
|
||||||
|
OP_EXPORT,
|
||||||
|
|
||||||
|
//for indexing
|
||||||
|
OP_INDEX,
|
||||||
|
OP_INDEX_ASSIGN,
|
||||||
|
OP_INDEX_ASSIGN_INTERMEDIATE,
|
||||||
|
OP_DOT,
|
||||||
|
|
||||||
|
//comparison of values
|
||||||
|
OP_COMPARE_EQUAL,
|
||||||
|
OP_COMPARE_NOT_EQUAL,
|
||||||
|
OP_COMPARE_LESS,
|
||||||
|
OP_COMPARE_LESS_EQUAL,
|
||||||
|
OP_COMPARE_GREATER,
|
||||||
|
OP_COMPARE_GREATER_EQUAL,
|
||||||
|
OP_INVERT, //for booleans
|
||||||
|
|
||||||
|
//logical operators
|
||||||
|
OP_AND,
|
||||||
|
OP_OR,
|
||||||
|
|
||||||
|
//jumps, and conditional jumps (absolute)
|
||||||
|
OP_JUMP,
|
||||||
|
OP_IF_FALSE_JUMP,
|
||||||
|
OP_FN_CALL,
|
||||||
|
OP_FN_RETURN,
|
||||||
|
|
||||||
|
//pop the stack at the end of a complex statement
|
||||||
|
OP_POP_STACK,
|
||||||
|
|
||||||
|
//meta
|
||||||
|
OP_FN_END, //different from SECTION_END
|
||||||
|
OP_SECTION_END = 255,
|
||||||
|
//TODO: add more
|
||||||
|
} Opcode;
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "toy_common.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "ast_node.h"
|
||||||
|
|
||||||
|
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
|
||||||
|
typedef struct {
|
||||||
|
Lexer* lexer;
|
||||||
|
bool error; //I've had an error
|
||||||
|
bool panic; //I am processing an error
|
||||||
|
|
||||||
|
//track the last two outputs from the lexer
|
||||||
|
Token current;
|
||||||
|
Token previous;
|
||||||
|
} Parser;
|
||||||
|
|
||||||
|
TOY_API void initParser(Parser* parser, Lexer* lexer);
|
||||||
|
TOY_API void freeParser(Parser* parser);
|
||||||
|
TOY_API ASTNode* scanParser(Parser* parser);
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
#include "scope.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
//run up the ancestor chain, freeing anything with 0 references left
|
||||||
|
static void freeAncestorChain(Scope* scope) {
|
||||||
|
scope->references--;
|
||||||
|
|
||||||
|
//free scope chain
|
||||||
|
if (scope->ancestor != NULL) {
|
||||||
|
freeAncestorChain(scope->ancestor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope->references > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralDictionary(&scope->variables);
|
||||||
|
freeLiteralDictionary(&scope->types);
|
||||||
|
|
||||||
|
FREE(Scope, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
//return false if invalid type
|
||||||
|
static bool checkType(Literal typeLiteral, Literal original, Literal value, bool constCheck) {
|
||||||
|
//for constants, fail if original != value
|
||||||
|
if (constCheck && AS_TYPE(typeLiteral).constant && !literalsAreEqual(original, value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//for any types
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't allow null types
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//always allow null values
|
||||||
|
if (IS_NULL(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//for each type, if a mismatch is found, return false
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY && !IS_ARRAY(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ARRAY(value)) {
|
||||||
|
//check value's type
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if null, assume it's a new entry
|
||||||
|
if (IS_NULL(original)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check children
|
||||||
|
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
|
||||||
|
if (AS_ARRAY(original)->count <= i) {
|
||||||
|
return true; //assume new entry pushed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(original)->literals[i], AS_ARRAY(value)->literals[i], constCheck)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY && !IS_DICTIONARY(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_DICTIONARY(value)) {
|
||||||
|
//check value's type
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if null, assume it's a new entry to a parent
|
||||||
|
if (IS_NULL(original)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check each child of value against the child of original
|
||||||
|
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
|
||||||
|
if (IS_NULL(AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//find the internal child of original that matches this child of value
|
||||||
|
_entry* ptr = NULL;
|
||||||
|
|
||||||
|
for (int j = 0; j < AS_DICTIONARY(original)->capacity; j++) {
|
||||||
|
if (literalsAreEqual(AS_DICTIONARY(original)->entries[j].key, AS_DICTIONARY(value)->entries[i].key)) {
|
||||||
|
ptr = &AS_DICTIONARY(original)->entries[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if not found, assume it's a new entry
|
||||||
|
if (!ptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check the type of key and value
|
||||||
|
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], ptr->key, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], ptr->value, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FUNCTION && !IS_FUNCTION(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//exposed functions
|
||||||
|
Scope* pushScope(Scope* ancestor) {
|
||||||
|
Scope* scope = ALLOCATE(Scope, 1);
|
||||||
|
scope->ancestor = ancestor;
|
||||||
|
initLiteralDictionary(&scope->variables);
|
||||||
|
initLiteralDictionary(&scope->types);
|
||||||
|
|
||||||
|
//tick up all scope reference counts
|
||||||
|
scope->references = 0;
|
||||||
|
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||||
|
ptr->references++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope* popScope(Scope* scope) {
|
||||||
|
if (scope == NULL) { //CAN pop a null
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope* ret = scope->ancestor;
|
||||||
|
|
||||||
|
//BUGFIX: when freeing a scope, free the function's scopes manually
|
||||||
|
for (int i = 0; i < scope->variables.capacity; i++) {
|
||||||
|
//handle keys, just in case
|
||||||
|
if (IS_FUNCTION(scope->variables.entries[i].key)) {
|
||||||
|
popScope(AS_FUNCTION(scope->variables.entries[i].key).scope);
|
||||||
|
AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_FUNCTION(scope->variables.entries[i].value)) {
|
||||||
|
popScope(AS_FUNCTION(scope->variables.entries[i].value).scope);
|
||||||
|
AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeAncestorChain(scope);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope* copyScope(Scope* original) {
|
||||||
|
Scope* scope = ALLOCATE(Scope, 1);
|
||||||
|
scope->ancestor = original->ancestor;
|
||||||
|
initLiteralDictionary(&scope->variables);
|
||||||
|
initLiteralDictionary(&scope->types);
|
||||||
|
|
||||||
|
//tick up all scope reference counts
|
||||||
|
scope->references = 0;
|
||||||
|
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||||
|
ptr->references++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy the contents of the dictionaries
|
||||||
|
for (int i = 0; i < original->variables.capacity; i++) {
|
||||||
|
if (!IS_NULL(original->variables.entries[i].key)) {
|
||||||
|
setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < original->types.capacity; i++) {
|
||||||
|
if (!IS_NULL(original->types.entries[i].key)) {
|
||||||
|
setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns false if error
|
||||||
|
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
|
||||||
|
//don't redefine a variable within this scope
|
||||||
|
if (existsLiteralDictionary(&scope->variables, key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//store the type, for later checking on assignment
|
||||||
|
setLiteralDictionary(&scope->types, key, type);
|
||||||
|
|
||||||
|
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
|
||||||
|
if (scope == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if it's not in this scope, keep searching up the chain
|
||||||
|
if (!existsLiteralDictionary(&scope->variables, key)) {
|
||||||
|
return isDelcaredScopeVariable(scope->ancestor, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return false if undefined, or can't be assigned
|
||||||
|
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
|
||||||
|
//dead end
|
||||||
|
if (scope == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if it's not in this scope, keep searching up the chain
|
||||||
|
if (!existsLiteralDictionary(&scope->variables, key)) {
|
||||||
|
return setScopeVariable(scope->ancestor, key, value, constCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
//type checking
|
||||||
|
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
|
||||||
|
Literal original = getLiteralDictionary(&scope->variables, key);
|
||||||
|
|
||||||
|
if (!checkType(typeLiteral, original, value, constCheck)) {
|
||||||
|
freeLiteral(typeLiteral);
|
||||||
|
freeLiteral(original);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//actually assign
|
||||||
|
setLiteralDictionary(&scope->variables, key, value);
|
||||||
|
|
||||||
|
freeLiteral(typeLiteral);
|
||||||
|
freeLiteral(original);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
|
||||||
|
//dead end
|
||||||
|
if (scope == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if it's not in this scope, keep searching up the chain
|
||||||
|
if (!existsLiteralDictionary(&scope->variables, key)) {
|
||||||
|
return getScopeVariable(scope->ancestor, key, valueHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
*valueHandle = getLiteralDictionary(&scope->variables, key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal getScopeType(Scope* scope, Literal key) {
|
||||||
|
//dead end
|
||||||
|
if (scope == NULL) {
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if it's not in this scope, keep searching up the chain
|
||||||
|
if (!existsLiteralDictionary(&scope->types, key)) {
|
||||||
|
return getScopeType(scope->ancestor, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getLiteralDictionary(&scope->types, key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "literal_array.h"
|
||||||
|
#include "literal_dictionary.h"
|
||||||
|
|
||||||
|
typedef struct Scope {
|
||||||
|
LiteralDictionary variables; //only allow identifiers as the keys
|
||||||
|
LiteralDictionary types; //the types, indexed by identifiers
|
||||||
|
struct Scope* ancestor;
|
||||||
|
int references; //how many scopes point here
|
||||||
|
} Scope;
|
||||||
|
|
||||||
|
Scope* pushScope(Scope* scope);
|
||||||
|
Scope* popScope(Scope* scope);
|
||||||
|
Scope* copyScope(Scope* original);
|
||||||
|
|
||||||
|
//returns false if error
|
||||||
|
bool declareScopeVariable(Scope* scope, Literal key, Literal type);
|
||||||
|
bool isDelcaredScopeVariable(Scope* scope, Literal key);
|
||||||
|
|
||||||
|
//return false if undefined
|
||||||
|
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
|
||||||
|
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
|
||||||
|
|
||||||
|
Literal getScopeType(Scope* scope, Literal key);
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum TokenType {
|
||||||
|
//types
|
||||||
|
TOKEN_NULL,
|
||||||
|
TOKEN_BOOLEAN,
|
||||||
|
TOKEN_INTEGER,
|
||||||
|
TOKEN_FLOAT,
|
||||||
|
TOKEN_STRING,
|
||||||
|
TOKEN_ARRAY,
|
||||||
|
TOKEN_DICTIONARY,
|
||||||
|
TOKEN_FUNCTION,
|
||||||
|
TOKEN_OPAQUE,
|
||||||
|
TOKEN_ANY,
|
||||||
|
|
||||||
|
//keywords and reserved words
|
||||||
|
TOKEN_AS,
|
||||||
|
TOKEN_ASSERT,
|
||||||
|
TOKEN_BREAK,
|
||||||
|
TOKEN_CLASS,
|
||||||
|
TOKEN_CONST,
|
||||||
|
TOKEN_CONTINUE,
|
||||||
|
TOKEN_DO,
|
||||||
|
TOKEN_ELSE,
|
||||||
|
TOKEN_EXPORT,
|
||||||
|
TOKEN_FOR,
|
||||||
|
TOKEN_FOREACH,
|
||||||
|
TOKEN_IF,
|
||||||
|
TOKEN_IMPORT,
|
||||||
|
TOKEN_IN,
|
||||||
|
TOKEN_OF,
|
||||||
|
TOKEN_PRINT,
|
||||||
|
TOKEN_RETURN,
|
||||||
|
TOKEN_TYPE,
|
||||||
|
TOKEN_ASTYPE,
|
||||||
|
TOKEN_TYPEOF,
|
||||||
|
TOKEN_VAR,
|
||||||
|
TOKEN_WHILE,
|
||||||
|
|
||||||
|
//literal values
|
||||||
|
TOKEN_IDENTIFIER,
|
||||||
|
TOKEN_LITERAL_TRUE,
|
||||||
|
TOKEN_LITERAL_FALSE,
|
||||||
|
TOKEN_LITERAL_INTEGER,
|
||||||
|
TOKEN_LITERAL_FLOAT,
|
||||||
|
TOKEN_LITERAL_STRING,
|
||||||
|
|
||||||
|
//math operators
|
||||||
|
TOKEN_PLUS,
|
||||||
|
TOKEN_MINUS,
|
||||||
|
TOKEN_MULTIPLY,
|
||||||
|
TOKEN_DIVIDE,
|
||||||
|
TOKEN_MODULO,
|
||||||
|
TOKEN_PLUS_ASSIGN,
|
||||||
|
TOKEN_MINUS_ASSIGN,
|
||||||
|
TOKEN_MULTIPLY_ASSIGN,
|
||||||
|
TOKEN_DIVIDE_ASSIGN,
|
||||||
|
TOKEN_MODULO_ASSIGN,
|
||||||
|
TOKEN_PLUS_PLUS,
|
||||||
|
TOKEN_MINUS_MINUS,
|
||||||
|
TOKEN_ASSIGN,
|
||||||
|
|
||||||
|
//logical operators
|
||||||
|
TOKEN_PAREN_LEFT,
|
||||||
|
TOKEN_PAREN_RIGHT,
|
||||||
|
TOKEN_BRACKET_LEFT,
|
||||||
|
TOKEN_BRACKET_RIGHT,
|
||||||
|
TOKEN_BRACE_LEFT,
|
||||||
|
TOKEN_BRACE_RIGHT,
|
||||||
|
TOKEN_NOT,
|
||||||
|
TOKEN_NOT_EQUAL,
|
||||||
|
TOKEN_EQUAL,
|
||||||
|
TOKEN_LESS,
|
||||||
|
TOKEN_GREATER,
|
||||||
|
TOKEN_LESS_EQUAL,
|
||||||
|
TOKEN_GREATER_EQUAL,
|
||||||
|
TOKEN_AND,
|
||||||
|
TOKEN_OR,
|
||||||
|
|
||||||
|
//other operators
|
||||||
|
TOKEN_COLON,
|
||||||
|
TOKEN_SEMICOLON,
|
||||||
|
TOKEN_COMMA,
|
||||||
|
TOKEN_DOT,
|
||||||
|
TOKEN_PIPE,
|
||||||
|
TOKEN_REST,
|
||||||
|
|
||||||
|
//meta tokens
|
||||||
|
TOKEN_PASS,
|
||||||
|
TOKEN_ERROR,
|
||||||
|
TOKEN_EOF,
|
||||||
|
} TokenType;
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
#include "toy_common.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//test variable sizes based on platform
|
||||||
|
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||||
|
|
||||||
|
STATIC_ASSERT(sizeof(char) == 1);
|
||||||
|
STATIC_ASSERT(sizeof(short) == 2);
|
||||||
|
STATIC_ASSERT(sizeof(int) == 4);
|
||||||
|
STATIC_ASSERT(sizeof(float) == 4);
|
||||||
|
STATIC_ASSERT(sizeof(unsigned char) == 1);
|
||||||
|
STATIC_ASSERT(sizeof(unsigned short) == 2);
|
||||||
|
STATIC_ASSERT(sizeof(unsigned int) == 4);
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
|
||||||
|
//declare the singleton
|
||||||
|
Command command;
|
||||||
|
|
||||||
|
void initCommand(int argc, const char* argv[]) {
|
||||||
|
//default values
|
||||||
|
command.error = false;
|
||||||
|
command.help = false;
|
||||||
|
command.version = false;
|
||||||
|
command.binaryfile = NULL;
|
||||||
|
command.sourcefile = NULL;
|
||||||
|
command.compilefile = NULL;
|
||||||
|
command.outfile = "out.tb";
|
||||||
|
command.source = NULL;
|
||||||
|
command.verbose = false;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||||
|
command.error = true; //error state by default, set to false by successful flags
|
||||||
|
|
||||||
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||||
|
command.help = true;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||||
|
command.version = true;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
||||||
|
command.verbose = true;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
|
||||||
|
command.sourcefile = (char*)argv[i + 1];
|
||||||
|
i++;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
||||||
|
command.source = (char*)argv[i + 1];
|
||||||
|
i++;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
|
||||||
|
command.compilefile = (char*)argv[i + 1];
|
||||||
|
i++;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
|
||||||
|
command.outfile = (char*)argv[i + 1];
|
||||||
|
i++;
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//option without a flag + ending in .tb = binary input
|
||||||
|
if (i < argc) {
|
||||||
|
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
|
||||||
|
command.binaryfile = (char*)argv[i];
|
||||||
|
command.error = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't keep reading in an error state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usageCommand(int argc, const char* argv[]) {
|
||||||
|
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helpCommand(int argc, const char* argv[]) {
|
||||||
|
usageCommand(argc, argv);
|
||||||
|
|
||||||
|
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||||
|
printf("-h\t| --help\t\tShow this help then exit.\n\n");
|
||||||
|
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
|
||||||
|
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
|
||||||
|
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
|
||||||
|
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
|
||||||
|
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
|
||||||
|
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyrightCommand(int argc, const char* argv[]) {
|
||||||
|
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||||
|
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
|
||||||
|
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
|
||||||
|
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
|
||||||
|
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
|
||||||
|
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
|
||||||
|
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define TOY_VERSION_MAJOR 0
|
||||||
|
#define TOY_VERSION_MINOR 6
|
||||||
|
#define TOY_VERSION_PATCH 1
|
||||||
|
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||||
|
|
||||||
|
//platform exports/imports
|
||||||
|
#if defined(__linux__)
|
||||||
|
#define TOY_API extern
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#elif defined(_WIN32) || defined(WIN32)
|
||||||
|
#define TOY_API
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define TOY_API
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TOY_EXPORT
|
||||||
|
//for processing the command line arguments
|
||||||
|
typedef struct {
|
||||||
|
bool error;
|
||||||
|
bool help;
|
||||||
|
bool version;
|
||||||
|
char* binaryfile;
|
||||||
|
char* sourcefile;
|
||||||
|
char* compilefile;
|
||||||
|
char* outfile; //defaults to out.tb
|
||||||
|
char* source;
|
||||||
|
bool verbose;
|
||||||
|
} Command;
|
||||||
|
|
||||||
|
extern Command command;
|
||||||
|
|
||||||
|
void initCommand(int argc, const char* argv[]);
|
||||||
|
|
||||||
|
void usageCommand(int argc, const char* argv[]);
|
||||||
|
void helpCommand(int argc, const char* argv[]);
|
||||||
|
void copyrightCommand(int argc, const char* argv[]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//NOTE: assigning to a byte from a short loses data
|
||||||
|
#define AS_USHORT(value) (*(unsigned short*)(&(value)))
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
CC=gcc
|
||||||
|
|
||||||
|
IDIR +=. ../source ../repl
|
||||||
|
CFLAGS +=$(addprefix -I,$(IDIR)) -g -Wall -W -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable
|
||||||
|
LIBS +=
|
||||||
|
|
||||||
|
ODIR = obj
|
||||||
|
TARGETS = $(wildcard ../source/*.c) $(wildcard ../repl/lib_*.c)
|
||||||
|
TESTS = $(wildcard *.c)
|
||||||
|
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
|
||||||
|
|
||||||
|
.PRECIOUS: $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
||||||
|
|
||||||
|
all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
||||||
|
|
||||||
|
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
|
||||||
|
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
|
||||||
|
ifeq ($(shell uname),Linux)
|
||||||
|
valgrind --leak-check=full --track-origins=yes $@
|
||||||
|
else
|
||||||
|
$@
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OBJ): | $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR):
|
||||||
|
mkdir $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR)/%.o: %.c
|
||||||
|
@$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
$(ODIR)/%.o: ../source/%.c
|
||||||
|
@$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(ODIR)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
//test operators (integers)
|
||||||
|
assert 1 + 1 == 2, "1 + 1 == 2";
|
||||||
|
assert 1 - 1 == 0, "1 - 1 == 0";
|
||||||
|
assert 2 * 2 == 4, "2 * 2 == 4";
|
||||||
|
assert 1 / 2 == 0, "1 / 2 == 0"; //integer division
|
||||||
|
assert 5 % 2 == 1, "5 % 2 == 1";
|
||||||
|
|
||||||
|
//test operators (floats)
|
||||||
|
assert 1.0 + 1.0 == 2.0, "1.0 + 1.0 == 2.0";
|
||||||
|
assert 1.0 - 1.0 == 0.0, "1.0 - 1.0 == 0.0";
|
||||||
|
assert 2.0 * 2.0 == 4.0, "2.0 * 2.0 == 4.0";
|
||||||
|
assert 1.0 / 2.0 == 0.5, "1.0 / 2.0 == 0.5";
|
||||||
|
|
||||||
|
|
||||||
|
var a = 10;
|
||||||
|
|
||||||
|
a += 20;
|
||||||
|
a -= 25;
|
||||||
|
|
||||||
|
assert a == 5, "+= or -= failed";
|
||||||
|
|
||||||
|
a *= 5;
|
||||||
|
a /= 2;
|
||||||
|
|
||||||
|
assert a == 12, "*= or /= failed";
|
||||||
|
|
||||||
|
a %= 8;
|
||||||
|
|
||||||
|
assert a == 4, "%= failed";
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//create a bunch of toy functions as literals to be called from C
|
||||||
|
|
||||||
|
fn answer() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn identity(x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn makeCounter() {
|
||||||
|
var total = 0;
|
||||||
|
|
||||||
|
fn counter() {
|
||||||
|
return ++total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail() {
|
||||||
|
assert false, "Failed correctly";
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//boolean origin
|
||||||
|
var b: bool = true;
|
||||||
|
|
||||||
|
assert bool b == true, "bool -> bool";
|
||||||
|
assert int b == 1, "bool -> int";
|
||||||
|
assert float b == 1, "bool -> float";
|
||||||
|
assert string b == "true", "bool -> string";
|
||||||
|
|
||||||
|
|
||||||
|
//integer origin
|
||||||
|
var i: int = 42;
|
||||||
|
|
||||||
|
assert bool i == true, "int -> bool";
|
||||||
|
assert int i == 42, "int -> int";
|
||||||
|
assert float i == 42, "int -> float";
|
||||||
|
assert string i == "42", "int -> string";
|
||||||
|
|
||||||
|
|
||||||
|
//float origin
|
||||||
|
var f: float = 3.14;
|
||||||
|
|
||||||
|
assert bool f == true, "float -> bool";
|
||||||
|
assert int f == 3, "float -> int";
|
||||||
|
assert float f == 3.14, "float -> float";
|
||||||
|
assert string f == "3.14", "float -> string";
|
||||||
|
|
||||||
|
|
||||||
|
//string origin
|
||||||
|
var s: string = "78.9";
|
||||||
|
|
||||||
|
assert bool s == true, "string -> bool";
|
||||||
|
assert int s == 78, "string -> int";
|
||||||
|
assert float s == 78.9, "string -> float";
|
||||||
|
assert string s == "78.9", "string -> string";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//test int -> float coercion
|
||||||
|
{
|
||||||
|
var f: float = 0;
|
||||||
|
|
||||||
|
assert typeof f == float, "coercion on decl failed";
|
||||||
|
|
||||||
|
f = 42;
|
||||||
|
|
||||||
|
assert typeof f == float, "coercion on assign failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//test numbers
|
||||||
|
assert 1 < 2, "1 < 2";
|
||||||
|
assert 1 == 1, "1 == 1";
|
||||||
|
assert 2 > 1, "2 > 1";
|
||||||
|
assert 1 <= 2, "1 <= 2";
|
||||||
|
assert 2 >= 1, "2 >= 1";
|
||||||
|
|
||||||
|
|
||||||
|
//test variables
|
||||||
|
var a = 1;
|
||||||
|
var b = 2;
|
||||||
|
|
||||||
|
assert a < b, "a < b";
|
||||||
|
assert a == a, "a == a";
|
||||||
|
assert b > a, "b > a";
|
||||||
|
assert a <= b, "a <= b";
|
||||||
|
assert b >= a, "b >= a";
|
||||||
|
|
||||||
|
|
||||||
|
//test negation
|
||||||
|
assert !false, "!false";
|
||||||
|
|
||||||
|
var c = false;
|
||||||
|
assert !c, "!c";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//dot product
|
||||||
|
var a = [1, 2, 3];
|
||||||
|
var b = [4, 5, 6];
|
||||||
|
|
||||||
|
assert _length(a) == _length(b), "a and b lengths are wrong";
|
||||||
|
|
||||||
|
var acc = 0;
|
||||||
|
for (var i = 0; i < _length(a); i++) {
|
||||||
|
acc += _get(a, i) * _get(b, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert acc == 32, "dot product failed";
|
||||||
|
|
||||||
|
|
||||||
|
//assume the args are matrices
|
||||||
|
fn matrix(first, second) {
|
||||||
|
//get the matrix size
|
||||||
|
var l1 = _length(first); //rows
|
||||||
|
var l2 = _length(_get(first, 0)); //cols
|
||||||
|
|
||||||
|
var l3 = _length(second); //rows
|
||||||
|
var l4 = _length(_get(second, 0)); //cols
|
||||||
|
|
||||||
|
//pre-allocate the matrix
|
||||||
|
var row = [];
|
||||||
|
for (var j = 0; j < l4; j++) {
|
||||||
|
_push(row, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < l1; i++) {
|
||||||
|
_push(result, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
//assign the values
|
||||||
|
for (var i = 0; i < _length(first); i++) {
|
||||||
|
//select each element of "first"
|
||||||
|
var firstElement = _get(first, i);
|
||||||
|
|
||||||
|
//for each element of second
|
||||||
|
for (var i2 = 0; i2 < _length(second); i2++) {
|
||||||
|
for (var j2 = 0; j2 < _length(_get(second, 0)); j2++) {
|
||||||
|
|
||||||
|
var val = _get(_get(first, i), i2) * _get(_get(second, i2), j2);
|
||||||
|
|
||||||
|
//TODO: needs better notation than this tmpRow variable
|
||||||
|
var tmpRow = _get(result, i);
|
||||||
|
_set(tmpRow, j2, val);
|
||||||
|
_set(result, i, tmpRow);
|
||||||
|
|
||||||
|
//result[ i ][ j2 ] += first[i][i2] * second[i2][j2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//matrix multiply
|
||||||
|
var c = [[4], [5], [6]]; //this is a 3x1
|
||||||
|
var d = [[1, 2, 3]]; //this is a 1x3
|
||||||
|
|
||||||
|
// c x d = 3x3
|
||||||
|
// d x c = 1x1
|
||||||
|
|
||||||
|
assert matrix(c, d) == [[4,8,12],[5,10,15],[6,12,18]], "Matrix multiplication failed";
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
NOTES: For some reason, this code results in the error:
|
||||||
|
Undeclared variable "inner"
|
||||||
|
|
||||||
|
It only occurs under these very specific conditions.
|
||||||
|
|
||||||
|
It appears to be a compiler issue, see issue #38 for more info.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn _getValue(self) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cache;
|
||||||
|
cache = 42.getValue(); //assignment, rather than declaration, allows the bug
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//test function chaining with the dot operator
|
||||||
|
|
||||||
|
fn _identity(self) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _check(self) {
|
||||||
|
assert self == 42, "dot chaining failed";
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
var val = 42;
|
||||||
|
|
||||||
|
val
|
||||||
|
.identity()
|
||||||
|
.check()
|
||||||
|
.identity()
|
||||||
|
.check()
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
//test the value is actually altered
|
||||||
|
fn _increment(self) {
|
||||||
|
return self + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert 3.increment().increment() == 5, "dot chaining increment failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
fn _add(self, inc) {
|
||||||
|
return self + inc;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert 1.add(2).add(3).add(4) == 10, "dottify bugfix failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
//test function return
|
||||||
|
fn testFourtyTwo() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert testFourtyTwo() == 42, "function returns failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test function parameters
|
||||||
|
fn identity(x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert identity("hello world") == "hello world", "identity function failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test closures
|
||||||
|
fn make() {
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
fn count() {
|
||||||
|
return ++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tally = make();
|
||||||
|
|
||||||
|
assert tally() == 1 && tally() == 2, "Closures failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test closures self-capture
|
||||||
|
fn capture(count: int) {
|
||||||
|
if (count > 5) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return capture(count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert capture(0) == 6, "Self capture failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test expressions as arguments
|
||||||
|
fn argFn() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outerFn(val) {
|
||||||
|
assert val == 42, "expression as argument failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
outerFn(argFn());
|
||||||
|
|
||||||
|
|
||||||
|
//test extra parameters
|
||||||
|
fn extra(one, two, ...rest) {
|
||||||
|
assert rest == ["three", "four", "five", "six", "seven"], "rest parameters failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
extra("one", "two", "three", "four", "five", "six", "seven");
|
||||||
|
|
||||||
|
|
||||||
|
//test underscore functions
|
||||||
|
fn _example(self, a, b, c) {
|
||||||
|
assert a == "a", "underscore failed (a)";
|
||||||
|
assert b == "b", "underscore failed (b)";
|
||||||
|
assert c == "c", "underscore failed (c)";
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert "hello world".example("a", "b", "c") == "hello world", "underscore call failed";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//test basic import/export
|
||||||
|
{
|
||||||
|
var variable: int = 42;
|
||||||
|
export variable as field;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
import field as value;
|
||||||
|
assert value == 42, "import/export failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test functions using import/export
|
||||||
|
{
|
||||||
|
fn f() {
|
||||||
|
import field;
|
||||||
|
|
||||||
|
assert field == 42, "import in function failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test importing/exporting of functions
|
||||||
|
{
|
||||||
|
fn func() {
|
||||||
|
return 69;
|
||||||
|
}
|
||||||
|
|
||||||
|
export func;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
import func;
|
||||||
|
assert func() == 69, "import/export of functions failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test that variables retain their types with the typeof keyword
|
||||||
|
{
|
||||||
|
var t: type = int;
|
||||||
|
|
||||||
|
export t;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
import t;
|
||||||
|
|
||||||
|
assert typeof t == type, "type retention failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
//test basic indexing
|
||||||
|
{
|
||||||
|
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||||
|
|
||||||
|
assert week[1] == "tuesday", "basic indexing failed (single element)";
|
||||||
|
|
||||||
|
assert week[1:1] == ["tuesday"], "basic indexing failed (single element as array)";
|
||||||
|
|
||||||
|
assert week[:] == ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"], "basic default indexing failed (first and second)";
|
||||||
|
assert week[::] == ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"], "basic default indexing failed (first, second and third)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test basic replacement
|
||||||
|
{
|
||||||
|
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||||
|
|
||||||
|
week[3:3] = "Holiday";
|
||||||
|
|
||||||
|
assert week == ["monday", "tuesday", "wednesday", "Holiday", "friday", "saturday", "sunday"], "basic replacement failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test backwards
|
||||||
|
{
|
||||||
|
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||||
|
|
||||||
|
assert week[::-1] == ["sunday", "saturday", "friday", "thursday", "wednesday", "tuesday", "monday"], "backwards failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test array weird manipulation
|
||||||
|
{
|
||||||
|
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||||
|
|
||||||
|
week[::-2] = ["first", "second", "third"];
|
||||||
|
|
||||||
|
assert week == ["monday", "tuesday", "third", "thursday", "second", "saturday", "first"], "array weird manipulation failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test index arithmetic
|
||||||
|
{
|
||||||
|
var a = [1, 2, 3];
|
||||||
|
|
||||||
|
a[1] *= 3;
|
||||||
|
|
||||||
|
assert a == [1, 6, 3], "index arithmetic failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test indexing with variables
|
||||||
|
{
|
||||||
|
var week = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
||||||
|
|
||||||
|
var first = 1;
|
||||||
|
var second = 2;
|
||||||
|
var third = -1;
|
||||||
|
|
||||||
|
assert week[first:second:third] == ["wednesday", "tuesday"], "indexing with variables failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test nested indexing
|
||||||
|
{
|
||||||
|
var a = [[[0]]];
|
||||||
|
|
||||||
|
a[0][0][0] = 42;
|
||||||
|
|
||||||
|
assert a[0][0] == [42], "nested indexing failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test combine example
|
||||||
|
{
|
||||||
|
fn combine(a, b, c) {
|
||||||
|
return [a, b, c];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert combine(1, 2, 3) == [1, 2, 3], "combine example failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//test basic insertion
|
||||||
|
{
|
||||||
|
var d = [:];
|
||||||
|
|
||||||
|
d["foo"] = "bar";
|
||||||
|
|
||||||
|
assert d == ["foo":"bar"], "basic insertion failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test index arithmetic
|
||||||
|
{
|
||||||
|
var d = ["one":1, "two":2, "three":3];
|
||||||
|
|
||||||
|
d["three"] *= 3;
|
||||||
|
|
||||||
|
assert d == ["one":1, "two":2, "three":9], "index arithmetic failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test indexing with variables
|
||||||
|
{
|
||||||
|
var d = ["one":1, "two":2, "three":3];
|
||||||
|
|
||||||
|
var first = "two";
|
||||||
|
|
||||||
|
assert d[first] == 2, "indexing with variables failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test nested indexing
|
||||||
|
{
|
||||||
|
var d = ["foo": ["bar": 0]];
|
||||||
|
|
||||||
|
d["foo"]["bar"] = 42;
|
||||||
|
|
||||||
|
print d;
|
||||||
|
|
||||||
|
assert d == ["foo": ["bar": 42]], "nested indexing failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//test basic indexing
|
||||||
|
var greeting: string = "hello world";
|
||||||
|
|
||||||
|
assert greeting[1] == "e", "basic default index failed (first)";
|
||||||
|
|
||||||
|
assert greeting[:] == "hello world", "basic default index failed (first and second)";
|
||||||
|
|
||||||
|
assert greeting[::] == "hello world", "basic default index failed (first, second & third)";
|
||||||
|
|
||||||
|
assert greeting[0:4] == "hello", "basic indexing failed";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//test basic replacement
|
||||||
|
greeting[0:4] = "goodnight";
|
||||||
|
|
||||||
|
assert greeting == "goodnight world", "basic replacement failed";
|
||||||
|
|
||||||
|
//test backwards string
|
||||||
|
assert greeting[::-1] == "dlrow thgindoog", "backwards string failed";
|
||||||
|
assert greeting[11:15:-1] == "dlrow", "backwards indexed string failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test string weird manipulation
|
||||||
|
var numbers = "0123456789";
|
||||||
|
|
||||||
|
numbers[::-2] = "abc";
|
||||||
|
|
||||||
|
assert numbers == "01234c6b8a", "string weird manipulation failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test indexing with variables
|
||||||
|
var first = 11;
|
||||||
|
var second = 15;
|
||||||
|
var third = -1;
|
||||||
|
|
||||||
|
assert greeting[first:second:third] == "dlrow", "indexing with variables failed";
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
fn sanity() {
|
||||||
|
//test true jump
|
||||||
|
if (true) {
|
||||||
|
assert true, "if-then failed (1)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false, "if-then failed (2)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test false jump
|
||||||
|
if (false) {
|
||||||
|
assert false, "if-then failed (3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert true, "if-then failed (4)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test while loop
|
||||||
|
var whileCounter = 0;
|
||||||
|
while (whileCounter < 10) {
|
||||||
|
whileCounter = whileCounter + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert whileCounter == 10, "while-loop failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test for loop
|
||||||
|
var forCache = 0;
|
||||||
|
for (var i = 0; i < 20; i++) {
|
||||||
|
forCache = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert forCache == 19, "for-loop failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test break - while
|
||||||
|
var breakWhileCache = 0;
|
||||||
|
while(true) {
|
||||||
|
breakWhileCache = breakWhileCache + 1;
|
||||||
|
|
||||||
|
if (breakWhileCache >= 7) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert breakWhileCache == 7, "break-while failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test continue - while
|
||||||
|
var continueWhileCache = 0;
|
||||||
|
while (continueWhileCache < 10) {
|
||||||
|
continueWhileCache = continueWhileCache + 1;
|
||||||
|
|
||||||
|
if (continueWhileCache >= 7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert continueWhileCache < 7, "continue-while failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test break - for
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
if (i >= 7) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert i < 7, "break-for failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test break - continue
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
if (i >= 7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert i < 7, "continue-for failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
print "All good";
|
||||||
|
}
|
||||||
|
|
||||||
|
//invoke the test
|
||||||
|
sanity();
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
//test true jump
|
||||||
|
if (true) {
|
||||||
|
assert true, "if-then failed (1)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false, "if-then failed (2)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test false jump
|
||||||
|
if (false) {
|
||||||
|
assert false, "if-then failed (3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert true, "if-then failed (4)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test while loop
|
||||||
|
var whileCounter = 0;
|
||||||
|
while (whileCounter < 10) {
|
||||||
|
whileCounter = whileCounter + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert whileCounter == 10, "while-loop failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test for loop
|
||||||
|
var forCache = 0;
|
||||||
|
for (var i = 0; i < 20; i++) {
|
||||||
|
forCache = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert forCache == 19, "for-loop failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test break - while
|
||||||
|
var breakWhileCache = 0;
|
||||||
|
while(true) {
|
||||||
|
breakWhileCache = breakWhileCache + 1;
|
||||||
|
|
||||||
|
if (breakWhileCache >= 7) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert breakWhileCache == 7, "break-while failed";
|
||||||
|
|
||||||
|
|
||||||
|
//test continue - while
|
||||||
|
var continueWhileCache = 0;
|
||||||
|
while (continueWhileCache < 10) {
|
||||||
|
continueWhileCache = continueWhileCache + 1;
|
||||||
|
|
||||||
|
if (continueWhileCache >= 7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert continueWhileCache < 7, "continue-while failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test break - for
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
if (i >= 7) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert i < 7, "break-for failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test break - continue
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
if (i >= 7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert i < 7, "continue-for failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//test the standard library, under a number of different circumstances
|
||||||
|
|
||||||
|
//test basic import
|
||||||
|
{
|
||||||
|
import standard;
|
||||||
|
|
||||||
|
//this depends on external factors, so only check the length
|
||||||
|
assert clock().length() == 24, "import library failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test import within a function
|
||||||
|
{
|
||||||
|
fn f() {
|
||||||
|
import standard;
|
||||||
|
|
||||||
|
assert clock != null, "import library within function failed";
|
||||||
|
|
||||||
|
return clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
//invoke
|
||||||
|
assert f()().length() == 24, "import library within function and return failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test closing over standard library element
|
||||||
|
{
|
||||||
|
import standard;
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
assert clock != null, "import library outside function failed";
|
||||||
|
|
||||||
|
return clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
//invoke
|
||||||
|
assert f()().length() == 24, "import library outside function and return failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test importing as an alias
|
||||||
|
{
|
||||||
|
import standard as std;
|
||||||
|
|
||||||
|
assert std["clock"]().length() == 24, "import library as alias failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//test the standard library
|
||||||
|
{
|
||||||
|
import standard;
|
||||||
|
|
||||||
|
//this depends on external factors, so only check the length
|
||||||
|
assert clock().length() == 24, "clock() import failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//test the timer library
|
||||||
|
{
|
||||||
|
//create a timer, run it for a short period
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
var timerA: opaque = startTimer();
|
||||||
|
for (var i: int = 0; i < 1000 * 1; i++);
|
||||||
|
var diffA: opaque = timerA.stopTimer();
|
||||||
|
|
||||||
|
//create another timer, run it for a longer period
|
||||||
|
var timerB: opaque = startTimer();
|
||||||
|
for (var i: int = 0; i < 1000 * 10; i++);
|
||||||
|
var diffB: opaque = timerB.stopTimer();
|
||||||
|
|
||||||
|
//check to ensure that the second timer took longer than the first
|
||||||
|
//WARNING: this has the small possibility of failing due to external factors
|
||||||
|
var difference: opaque = diffA.compareTimer(diffB);
|
||||||
|
|
||||||
|
assert difference.getTimerSeconds() >= 0, "compareTimer() (seconds) failed";
|
||||||
|
if (difference.getTimerSeconds() == 0) {
|
||||||
|
assert difference.getTimerMicroseconds() >= 0, "compareTimer() (microseconds) failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
//all timers must be destroyed after use
|
||||||
|
timerA.destroyTimer();
|
||||||
|
timerB.destroyTimer();
|
||||||
|
diffA.destroyTimer();
|
||||||
|
diffB.destroyTimer();
|
||||||
|
difference.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//create a timer, manipulate it's values
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
//set the timer values manually
|
||||||
|
var timer: opaque = createTimer(42, 8891);
|
||||||
|
|
||||||
|
//check the timer values
|
||||||
|
assert timer.getTimerSeconds() == 42, "getTimerSeconds() failed";
|
||||||
|
assert timer.getTimerMicroseconds() == 8891, "getTimerMicroseconds() failed";
|
||||||
|
|
||||||
|
//all timers must be destroyed after use
|
||||||
|
timer.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//set a timer to check string representation
|
||||||
|
import timer;
|
||||||
|
|
||||||
|
var timer: opaque = createTimer(42, 999);
|
||||||
|
|
||||||
|
assert timer.timerToString() == "42.000999", "timerToString() failed";
|
||||||
|
|
||||||
|
//all timers must be destroyed after use
|
||||||
|
timer.destroyTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
assert true && true, "boolean and failed";
|
||||||
|
assert true || true, "boolean or failed";
|
||||||
|
|
||||||
|
assert false || true && true, "boolen precedence failed";
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
var arr: [string] = [
|
||||||
|
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
|
"8",
|
||||||
|
"9",
|
||||||
|
|
||||||
|
"10",
|
||||||
|
"11",
|
||||||
|
"12",
|
||||||
|
"13",
|
||||||
|
"14",
|
||||||
|
"15",
|
||||||
|
"16",
|
||||||
|
"17",
|
||||||
|
"18",
|
||||||
|
"19",
|
||||||
|
|
||||||
|
"20",
|
||||||
|
"21",
|
||||||
|
"22",
|
||||||
|
"23",
|
||||||
|
"24",
|
||||||
|
"25",
|
||||||
|
"26",
|
||||||
|
"27",
|
||||||
|
"28",
|
||||||
|
"29",
|
||||||
|
|
||||||
|
"30",
|
||||||
|
"31",
|
||||||
|
"32",
|
||||||
|
"33",
|
||||||
|
"34",
|
||||||
|
"35",
|
||||||
|
"36",
|
||||||
|
"37",
|
||||||
|
"38",
|
||||||
|
"39",
|
||||||
|
|
||||||
|
"40",
|
||||||
|
"41",
|
||||||
|
"42",
|
||||||
|
"43",
|
||||||
|
"44",
|
||||||
|
"45",
|
||||||
|
"46",
|
||||||
|
"47",
|
||||||
|
"48",
|
||||||
|
"49",
|
||||||
|
|
||||||
|
"50",
|
||||||
|
"51",
|
||||||
|
"52",
|
||||||
|
"53",
|
||||||
|
"54",
|
||||||
|
"55",
|
||||||
|
"56",
|
||||||
|
"57",
|
||||||
|
"58",
|
||||||
|
"59",
|
||||||
|
|
||||||
|
"60",
|
||||||
|
"61",
|
||||||
|
"62",
|
||||||
|
"63",
|
||||||
|
"64",
|
||||||
|
"65",
|
||||||
|
"66",
|
||||||
|
"67",
|
||||||
|
"68",
|
||||||
|
"69",
|
||||||
|
|
||||||
|
"70",
|
||||||
|
"71",
|
||||||
|
"72",
|
||||||
|
"73",
|
||||||
|
"74",
|
||||||
|
"75",
|
||||||
|
"76",
|
||||||
|
"77",
|
||||||
|
"78",
|
||||||
|
"79",
|
||||||
|
|
||||||
|
"80",
|
||||||
|
"81",
|
||||||
|
"82",
|
||||||
|
"83",
|
||||||
|
"84",
|
||||||
|
"85",
|
||||||
|
"86",
|
||||||
|
"87",
|
||||||
|
"88",
|
||||||
|
"89",
|
||||||
|
|
||||||
|
"90",
|
||||||
|
"91",
|
||||||
|
"92",
|
||||||
|
"93",
|
||||||
|
"94",
|
||||||
|
"95",
|
||||||
|
"96",
|
||||||
|
"97",
|
||||||
|
"98",
|
||||||
|
"99",
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
"100",
|
||||||
|
"101",
|
||||||
|
"102",
|
||||||
|
"103",
|
||||||
|
"104",
|
||||||
|
"105",
|
||||||
|
"106",
|
||||||
|
"107",
|
||||||
|
"108",
|
||||||
|
"109",
|
||||||
|
|
||||||
|
"110",
|
||||||
|
"111",
|
||||||
|
"112",
|
||||||
|
"113",
|
||||||
|
"114",
|
||||||
|
"115",
|
||||||
|
"116",
|
||||||
|
"117",
|
||||||
|
"118",
|
||||||
|
"119",
|
||||||
|
|
||||||
|
"120",
|
||||||
|
"121",
|
||||||
|
"122",
|
||||||
|
"123",
|
||||||
|
"124",
|
||||||
|
"125",
|
||||||
|
"126",
|
||||||
|
"127",
|
||||||
|
"128",
|
||||||
|
"129",
|
||||||
|
|
||||||
|
"130",
|
||||||
|
"131",
|
||||||
|
"132",
|
||||||
|
"133",
|
||||||
|
"134",
|
||||||
|
"135",
|
||||||
|
"136",
|
||||||
|
"137",
|
||||||
|
"138",
|
||||||
|
"139",
|
||||||
|
|
||||||
|
"140",
|
||||||
|
"141",
|
||||||
|
"142",
|
||||||
|
"143",
|
||||||
|
"144",
|
||||||
|
"145",
|
||||||
|
"146",
|
||||||
|
"147",
|
||||||
|
"148",
|
||||||
|
"149",
|
||||||
|
|
||||||
|
"150",
|
||||||
|
"151",
|
||||||
|
"152",
|
||||||
|
"153",
|
||||||
|
"154",
|
||||||
|
"155",
|
||||||
|
"156",
|
||||||
|
"157",
|
||||||
|
"158",
|
||||||
|
"159",
|
||||||
|
|
||||||
|
"160",
|
||||||
|
"161",
|
||||||
|
"162",
|
||||||
|
"163",
|
||||||
|
"164",
|
||||||
|
"165",
|
||||||
|
"166",
|
||||||
|
"167",
|
||||||
|
"168",
|
||||||
|
"169",
|
||||||
|
|
||||||
|
"170",
|
||||||
|
"171",
|
||||||
|
"172",
|
||||||
|
"173",
|
||||||
|
"174",
|
||||||
|
"175",
|
||||||
|
"176",
|
||||||
|
"177",
|
||||||
|
"178",
|
||||||
|
"179",
|
||||||
|
|
||||||
|
"180",
|
||||||
|
"181",
|
||||||
|
"182",
|
||||||
|
"183",
|
||||||
|
"184",
|
||||||
|
"185",
|
||||||
|
"186",
|
||||||
|
"187",
|
||||||
|
"188",
|
||||||
|
"189",
|
||||||
|
|
||||||
|
"190",
|
||||||
|
"191",
|
||||||
|
"192",
|
||||||
|
"193",
|
||||||
|
"194",
|
||||||
|
"195",
|
||||||
|
"196",
|
||||||
|
"197",
|
||||||
|
"198",
|
||||||
|
"199",
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
"200",
|
||||||
|
"201",
|
||||||
|
"202",
|
||||||
|
"203",
|
||||||
|
"204",
|
||||||
|
"205",
|
||||||
|
"206",
|
||||||
|
"207",
|
||||||
|
"208",
|
||||||
|
"209",
|
||||||
|
|
||||||
|
"210",
|
||||||
|
"211",
|
||||||
|
"212",
|
||||||
|
"213",
|
||||||
|
"214",
|
||||||
|
"215",
|
||||||
|
"216",
|
||||||
|
"217",
|
||||||
|
"218",
|
||||||
|
"219",
|
||||||
|
|
||||||
|
"220",
|
||||||
|
"221",
|
||||||
|
"222",
|
||||||
|
"223",
|
||||||
|
"224",
|
||||||
|
"225",
|
||||||
|
"226",
|
||||||
|
"227",
|
||||||
|
"228",
|
||||||
|
"229",
|
||||||
|
|
||||||
|
"230",
|
||||||
|
"231",
|
||||||
|
"232",
|
||||||
|
"233",
|
||||||
|
"234",
|
||||||
|
"235",
|
||||||
|
"236",
|
||||||
|
"237",
|
||||||
|
"238",
|
||||||
|
"239",
|
||||||
|
|
||||||
|
"240",
|
||||||
|
"241",
|
||||||
|
"242",
|
||||||
|
"243",
|
||||||
|
"244",
|
||||||
|
"245",
|
||||||
|
"246",
|
||||||
|
"247",
|
||||||
|
"248",
|
||||||
|
"249",
|
||||||
|
|
||||||
|
"250",
|
||||||
|
"251",
|
||||||
|
"252",
|
||||||
|
"253",
|
||||||
|
"254",
|
||||||
|
"255",
|
||||||
|
"256",
|
||||||
|
"257",
|
||||||
|
"258",
|
||||||
|
"259",
|
||||||
|
|
||||||
|
"260",
|
||||||
|
"261",
|
||||||
|
"262",
|
||||||
|
"263",
|
||||||
|
"264",
|
||||||
|
"265",
|
||||||
|
"266",
|
||||||
|
"267",
|
||||||
|
"268",
|
||||||
|
"269",
|
||||||
|
|
||||||
|
"270",
|
||||||
|
"271",
|
||||||
|
"272",
|
||||||
|
"273",
|
||||||
|
"274",
|
||||||
|
"275",
|
||||||
|
"276",
|
||||||
|
"277",
|
||||||
|
"278",
|
||||||
|
"279",
|
||||||
|
|
||||||
|
"280",
|
||||||
|
"281",
|
||||||
|
"282",
|
||||||
|
"283",
|
||||||
|
"284",
|
||||||
|
"285",
|
||||||
|
"286",
|
||||||
|
"287",
|
||||||
|
"288",
|
||||||
|
"289",
|
||||||
|
|
||||||
|
"290",
|
||||||
|
"291",
|
||||||
|
"292",
|
||||||
|
"293",
|
||||||
|
"294",
|
||||||
|
"295",
|
||||||
|
"296",
|
||||||
|
"297",
|
||||||
|
"298",
|
||||||
|
"299"
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
print arr;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//NOTE: dictionaries are NOT stored in order
|
||||||
|
var dict: [string : string] = ["0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","10":"10","11":"11","12":"12","13":"13","14":"14","15":"15","16":"16","17":"17","18":"18","19":"19","20":"20","21":"21","22":"22","23":"23","24":"24","25":"25","26":"26","27":"27","28":"28","29":"29","30":"30","31":"31","32":"32","33":"33","34":"34","35":"35","36":"36","37":"37","38":"38","39":"39","40":"40","41":"41","42":"42","43":"43","44":"44","45":"45","46":"46","47":"47","48":"48","49":"49","50":"50","51":"51","52":"52","53":"53","54":"54","55":"55","56":"56","57":"57","58":"58","59":"59","60":"60","61":"61","62":"62","63":"63","64":"64","65":"65","66":"66","67":"67","68":"68","69":"69","70":"70","71":"71","72":"72","73":"73","74":"74","75":"75","76":"76","77":"77","78":"78","79":"79","80":"80","81":"81","82":"82","83":"83","84":"84","85":"85","86":"86","87":"87","88":"88","89":"89","90":"90","91":"91","92":"92","93":"93","94":"94","95":"95","96":"96","97":"97","98":"98","99":"99","100":"100","101":"101","102":"102","103":"103","104":"104","105":"105","106":"106","107":"107","108":"108","109":"109","110":"110","111":"111","112":"112","113":"113","114":"114","115":"115","116":"116","117":"117","118":"118","119":"119","120":"120","121":"121","122":"122","123":"123","124":"124","125":"125","126":"126","127":"127","128":"128","129":"129","130":"130","131":"131","132":"132","133":"133","134":"134","135":"135","136":"136","137":"137","138":"138","139":"139","140":"140","141":"141","142":"142","143":"143","144":"144","145":"145","146":"146","147":"147","148":"148","149":"149","150":"150","151":"151","152":"152","153":"153","154":"154","155":"155","156":"156","157":"157","158":"158","159":"159","160":"160","161":"161","162":"162","163":"163","164":"164","165":"165","166":"166","167":"167","168":"168","169":"169","170":"170","171":"171","172":"172","173":"173","174":"174","175":"175","176":"176","177":"177","178":"178","179":"179","180":"180","181":"181","182":"182","183":"183","184":"184","185":"185","186":"186","187":"187","188":"188","189":"189","190":"190","191":"191","192":"192","193":"193","194":"194","195":"195","196":"196","197":"197","198":"198","199":"199","200":"200","201":"201","202":"202","203":"203","204":"204","205":"205","206":"206","207":"207","208":"208","209":"209","210":"210","211":"211","212":"212","213":"213","214":"214","215":"215","216":"216","217":"217","218":"218","219":"219","220":"220","221":"221","222":"222","223":"223","224":"224","225":"225","226":"226","227":"227","228":"228","229":"229","230":"230","231":"231","232":"232","233":"233","234":"234","235":"235","236":"236","237":"237","238":"238","239":"239","240":"240","241":"241","242":"242","243":"243","244":"244","245":"245","246":"246","247":"247","248":"248","249":"249","250":"250","251":"251","252":"252","253":"253","254":"254","255":"255","256":"256","257":"257","258":"258","259":"259","260":"260","261":"261","262":"262","263":"263","264":"264","265":"265","266":"266","267":"267","268":"268","269":"269","270":"270","271":"271","272":"272","273":"273","274":"274","275":"275","276":"276","277":"277","278":"278","279":"279","280":"280","281":"281","282":"282","283":"283","284":"284","285":"285","286":"286","287":"287","288":"288","289":"289","290":"290","291":"291","292":"292","293":"293","294":"294","295":"295","296":"296","297":"297","298":"298","299":"299"];
|
||||||
|
|
||||||
|
print dict;
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
print 0;
|
||||||
|
print 1;
|
||||||
|
print 2;
|
||||||
|
print 3;
|
||||||
|
print 4;
|
||||||
|
print 5;
|
||||||
|
print 6;
|
||||||
|
print 7;
|
||||||
|
print 8;
|
||||||
|
print 9;
|
||||||
|
|
||||||
|
print 10;
|
||||||
|
print 11;
|
||||||
|
print 12;
|
||||||
|
print 13;
|
||||||
|
print 14;
|
||||||
|
print 15;
|
||||||
|
print 16;
|
||||||
|
print 17;
|
||||||
|
print 18;
|
||||||
|
print 19;
|
||||||
|
|
||||||
|
print 20;
|
||||||
|
print 21;
|
||||||
|
print 22;
|
||||||
|
print 23;
|
||||||
|
print 24;
|
||||||
|
print 25;
|
||||||
|
print 26;
|
||||||
|
print 27;
|
||||||
|
print 28;
|
||||||
|
print 29;
|
||||||
|
|
||||||
|
print 30;
|
||||||
|
print 31;
|
||||||
|
print 32;
|
||||||
|
print 33;
|
||||||
|
print 34;
|
||||||
|
print 35;
|
||||||
|
print 36;
|
||||||
|
print 37;
|
||||||
|
print 38;
|
||||||
|
print 39;
|
||||||
|
|
||||||
|
print 40;
|
||||||
|
print 41;
|
||||||
|
print 42;
|
||||||
|
print 43;
|
||||||
|
print 44;
|
||||||
|
print 45;
|
||||||
|
print 46;
|
||||||
|
print 47;
|
||||||
|
print 48;
|
||||||
|
print 49;
|
||||||
|
|
||||||
|
print 50;
|
||||||
|
print 51;
|
||||||
|
print 52;
|
||||||
|
print 53;
|
||||||
|
print 54;
|
||||||
|
print 55;
|
||||||
|
print 56;
|
||||||
|
print 57;
|
||||||
|
print 58;
|
||||||
|
print 59;
|
||||||
|
|
||||||
|
print 60;
|
||||||
|
print 61;
|
||||||
|
print 62;
|
||||||
|
print 63;
|
||||||
|
print 64;
|
||||||
|
print 65;
|
||||||
|
print 66;
|
||||||
|
print 67;
|
||||||
|
print 68;
|
||||||
|
print 69;
|
||||||
|
|
||||||
|
print 70;
|
||||||
|
print 71;
|
||||||
|
print 72;
|
||||||
|
print 73;
|
||||||
|
print 74;
|
||||||
|
print 75;
|
||||||
|
print 76;
|
||||||
|
print 77;
|
||||||
|
print 78;
|
||||||
|
print 79;
|
||||||
|
|
||||||
|
print 80;
|
||||||
|
print 81;
|
||||||
|
print 82;
|
||||||
|
print 83;
|
||||||
|
print 84;
|
||||||
|
print 85;
|
||||||
|
print 86;
|
||||||
|
print 87;
|
||||||
|
print 88;
|
||||||
|
print 89;
|
||||||
|
|
||||||
|
print 90;
|
||||||
|
print 91;
|
||||||
|
print 92;
|
||||||
|
print 93;
|
||||||
|
print 94;
|
||||||
|
print 95;
|
||||||
|
print 96;
|
||||||
|
print 97;
|
||||||
|
print 98;
|
||||||
|
print 99;
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
print 100;
|
||||||
|
print 101;
|
||||||
|
print 102;
|
||||||
|
print 103;
|
||||||
|
print 104;
|
||||||
|
print 105;
|
||||||
|
print 106;
|
||||||
|
print 107;
|
||||||
|
print 108;
|
||||||
|
print 109;
|
||||||
|
|
||||||
|
print 110;
|
||||||
|
print 111;
|
||||||
|
print 112;
|
||||||
|
print 113;
|
||||||
|
print 114;
|
||||||
|
print 115;
|
||||||
|
print 116;
|
||||||
|
print 117;
|
||||||
|
print 118;
|
||||||
|
print 119;
|
||||||
|
|
||||||
|
print 120;
|
||||||
|
print 121;
|
||||||
|
print 122;
|
||||||
|
print 123;
|
||||||
|
print 124;
|
||||||
|
print 125;
|
||||||
|
print 126;
|
||||||
|
print 127;
|
||||||
|
print 128;
|
||||||
|
print 129;
|
||||||
|
|
||||||
|
print 130;
|
||||||
|
print 131;
|
||||||
|
print 132;
|
||||||
|
print 133;
|
||||||
|
print 134;
|
||||||
|
print 135;
|
||||||
|
print 136;
|
||||||
|
print 137;
|
||||||
|
print 138;
|
||||||
|
print 139;
|
||||||
|
|
||||||
|
print 140;
|
||||||
|
print 141;
|
||||||
|
print 142;
|
||||||
|
print 143;
|
||||||
|
print 144;
|
||||||
|
print 145;
|
||||||
|
print 146;
|
||||||
|
print 147;
|
||||||
|
print 148;
|
||||||
|
print 149;
|
||||||
|
|
||||||
|
print 150;
|
||||||
|
print 151;
|
||||||
|
print 152;
|
||||||
|
print 153;
|
||||||
|
print 154;
|
||||||
|
print 155;
|
||||||
|
print 156;
|
||||||
|
print 157;
|
||||||
|
print 158;
|
||||||
|
print 159;
|
||||||
|
|
||||||
|
print 160;
|
||||||
|
print 161;
|
||||||
|
print 162;
|
||||||
|
print 163;
|
||||||
|
print 164;
|
||||||
|
print 165;
|
||||||
|
print 166;
|
||||||
|
print 167;
|
||||||
|
print 168;
|
||||||
|
print 169;
|
||||||
|
|
||||||
|
print 170;
|
||||||
|
print 171;
|
||||||
|
print 172;
|
||||||
|
print 173;
|
||||||
|
print 174;
|
||||||
|
print 175;
|
||||||
|
print 176;
|
||||||
|
print 177;
|
||||||
|
print 178;
|
||||||
|
print 179;
|
||||||
|
|
||||||
|
print 180;
|
||||||
|
print 181;
|
||||||
|
print 182;
|
||||||
|
print 183;
|
||||||
|
print 184;
|
||||||
|
print 185;
|
||||||
|
print 186;
|
||||||
|
print 187;
|
||||||
|
print 188;
|
||||||
|
print 189;
|
||||||
|
|
||||||
|
print 190;
|
||||||
|
print 191;
|
||||||
|
print 192;
|
||||||
|
print 193;
|
||||||
|
print 194;
|
||||||
|
print 195;
|
||||||
|
print 196;
|
||||||
|
print 197;
|
||||||
|
print 198;
|
||||||
|
print 199;
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
print 200;
|
||||||
|
print 201;
|
||||||
|
print 202;
|
||||||
|
print 203;
|
||||||
|
print 204;
|
||||||
|
print 205;
|
||||||
|
print 206;
|
||||||
|
print 207;
|
||||||
|
print 208;
|
||||||
|
print 209;
|
||||||
|
|
||||||
|
print 210;
|
||||||
|
print 211;
|
||||||
|
print 212;
|
||||||
|
print 213;
|
||||||
|
print 214;
|
||||||
|
print 215;
|
||||||
|
print 216;
|
||||||
|
print 217;
|
||||||
|
print 218;
|
||||||
|
print 219;
|
||||||
|
|
||||||
|
print 220;
|
||||||
|
print 221;
|
||||||
|
print 222;
|
||||||
|
print 223;
|
||||||
|
print 224;
|
||||||
|
print 225;
|
||||||
|
print 226;
|
||||||
|
print 227;
|
||||||
|
print 228;
|
||||||
|
print 229;
|
||||||
|
|
||||||
|
print 230;
|
||||||
|
print 231;
|
||||||
|
print 232;
|
||||||
|
print 233;
|
||||||
|
print 234;
|
||||||
|
print 235;
|
||||||
|
print 236;
|
||||||
|
print 237;
|
||||||
|
print 238;
|
||||||
|
print 239;
|
||||||
|
|
||||||
|
print 240;
|
||||||
|
print 241;
|
||||||
|
print 242;
|
||||||
|
print 243;
|
||||||
|
print 244;
|
||||||
|
print 245;
|
||||||
|
print 246;
|
||||||
|
print 247;
|
||||||
|
print 248;
|
||||||
|
print 249;
|
||||||
|
|
||||||
|
print 250;
|
||||||
|
print 251;
|
||||||
|
print 252;
|
||||||
|
print 253;
|
||||||
|
print 254;
|
||||||
|
print 255;
|
||||||
|
print 256;
|
||||||
|
print 257;
|
||||||
|
print 258;
|
||||||
|
print 259;
|
||||||
|
|
||||||
|
print 260;
|
||||||
|
print 261;
|
||||||
|
print 262;
|
||||||
|
print 263;
|
||||||
|
print 264;
|
||||||
|
print 265;
|
||||||
|
print 266;
|
||||||
|
print 267;
|
||||||
|
print 268;
|
||||||
|
print 269;
|
||||||
|
|
||||||
|
print 270;
|
||||||
|
print 271;
|
||||||
|
print 272;
|
||||||
|
print 273;
|
||||||
|
print 274;
|
||||||
|
print 275;
|
||||||
|
print 276;
|
||||||
|
print 277;
|
||||||
|
print 278;
|
||||||
|
print 279;
|
||||||
|
|
||||||
|
print 280;
|
||||||
|
print 281;
|
||||||
|
print 282;
|
||||||
|
print 283;
|
||||||
|
print 284;
|
||||||
|
print 285;
|
||||||
|
print 286;
|
||||||
|
print 287;
|
||||||
|
print 288;
|
||||||
|
print 289;
|
||||||
|
|
||||||
|
print 290;
|
||||||
|
print 291;
|
||||||
|
print 292;
|
||||||
|
print 293;
|
||||||
|
print 294;
|
||||||
|
print 295;
|
||||||
|
print 296;
|
||||||
|
print 297;
|
||||||
|
print 298;
|
||||||
|
print 299;
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
{
|
||||||
|
//test arrays without types
|
||||||
|
var array = [];
|
||||||
|
|
||||||
|
assert _length(array) == 0, "_length failed with array";
|
||||||
|
|
||||||
|
_push(array, 1);
|
||||||
|
_push(array, 2);
|
||||||
|
_push(array, 3);
|
||||||
|
_push(array, 4);
|
||||||
|
_push(array, "foo");
|
||||||
|
|
||||||
|
assert _length(array) == 5, "_push failed with array";
|
||||||
|
assert _pop(array) == "foo", "_pop failed with array";
|
||||||
|
|
||||||
|
_set(array, 2, "bar");
|
||||||
|
assert array == [1, 2, "bar", 4], "_set failed with array";
|
||||||
|
assert _get(array, 3) == 4, "_get failed with array";
|
||||||
|
|
||||||
|
|
||||||
|
//test dictionaries without types
|
||||||
|
var dict = [:];
|
||||||
|
|
||||||
|
_set(dict, "key", "value");
|
||||||
|
_set(dict, 1, 2);
|
||||||
|
|
||||||
|
assert dict == ["key":"value", 1:2], "_set failed with dictionaries";
|
||||||
|
assert _get(dict, "key") == "value", "_get failed with dictionaries";
|
||||||
|
|
||||||
|
|
||||||
|
//test _length
|
||||||
|
assert _length(array) == 4 && _length(dict) == 2, "_length failed with array or dictionaries";
|
||||||
|
|
||||||
|
|
||||||
|
//test clear
|
||||||
|
_clear(array);
|
||||||
|
_clear(dict);
|
||||||
|
|
||||||
|
assert _length(array) == 0 && _length(dict) == 0, "_clear failed with array or dictionaries";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
//test arrays with types
|
||||||
|
var array: [int] = [];
|
||||||
|
|
||||||
|
assert _length(array) == 0, "_length failed with array (+ types)";
|
||||||
|
|
||||||
|
_push(array, 1);
|
||||||
|
_push(array, 2);
|
||||||
|
_push(array, 3);
|
||||||
|
_push(array, 4);
|
||||||
|
_push(array, 10);
|
||||||
|
|
||||||
|
assert _length(array) == 5, "_push or failed with array (+ types)";
|
||||||
|
assert _pop(array) == 10, "_pop failed with array (+ types)";
|
||||||
|
|
||||||
|
_set(array, 2, 70);
|
||||||
|
assert array == [1, 2, 70, 4], "_set failed with array (+ types)";
|
||||||
|
assert _get(array, 3) == 4, "_get failed with array (+ types)";
|
||||||
|
|
||||||
|
|
||||||
|
//test dictionaries with types
|
||||||
|
var dict: [string : string] = [:];
|
||||||
|
|
||||||
|
_set(dict, "key", "value");
|
||||||
|
|
||||||
|
assert dict == ["key":"value"], "_set failed with dictionaries (+ types)";
|
||||||
|
assert _get(dict, "key") == "value", "_get failed with dictionaries (+ types)";
|
||||||
|
|
||||||
|
|
||||||
|
//test length with types
|
||||||
|
assert _length(array) == 4 && _length(dict) == 1, "_length failed with array or dictionaries (+ types)";
|
||||||
|
|
||||||
|
|
||||||
|
//test clear with types
|
||||||
|
_clear(array);
|
||||||
|
_clear(dict);
|
||||||
|
|
||||||
|
assert _length(array) == 0 && _length(dict) == 0, "_clear failed with array or dictionaries (+ types)";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = "hello world";
|
||||||
|
|
||||||
|
assert _length(str) == 11, "_length failed with string";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//test the opaque data type works
|
||||||
|
|
||||||
|
var o: opaque = produce();
|
||||||
|
|
||||||
|
consume(o);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
fn panic() {
|
||||||
|
assert false, "!ignore panicking within a function";
|
||||||
|
}
|
||||||
|
|
||||||
|
panic();
|
||||||
|
panic();
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var complex: type = astype [string: [int]];
|
||||||
|
var deep: type = astype [[[ int ]]];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//test exports
|
||||||
|
var field: int = 42;
|
||||||
|
|
||||||
|
fn function() {
|
||||||
|
return 69;
|
||||||
|
}
|
||||||
|
|
||||||
|
export field;
|
||||||
|
export function;
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//test imports
|
||||||
|
import field;
|
||||||
|
//import function;
|
||||||
|
|
||||||
|
//assert field == 42, "import field failed";
|
||||||
|
|
||||||
|
//assert function() == 69, "import function failed";
|
||||||
|
|
||||||
|
print "All good";
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//basic types
|
||||||
|
var t: type = int;
|
||||||
|
var u: t = 42;
|
||||||
|
|
||||||
|
assert t == int, "types are not first class";
|
||||||
|
assert u == 42, "first-class types are screwing with values";
|
||||||
|
|
||||||
|
|
||||||
|
//differentiate by the "type" value
|
||||||
|
var v: type = astype [int];
|
||||||
|
var w = [int];
|
||||||
|
var x = v;
|
||||||
|
|
||||||
|
assert w == [int], "defining an array of types failed";
|
||||||
|
assert x == astype [int], "re-assigning a type value failed";
|
||||||
|
|
||||||
|
//complex type
|
||||||
|
var complex: type = astype [string : [int]];
|
||||||
|
var dict: complex = [
|
||||||
|
"first array": [1, 2, 3],
|
||||||
|
"second array": [4, 5, 6],
|
||||||
|
"third array": [7, 8, 9]
|
||||||
|
];
|
||||||
|
|
||||||
|
print "All good";
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "ast_node.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
//test literals
|
||||||
|
char* str = "foobar";
|
||||||
|
|
||||||
|
Literal literal = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str));
|
||||||
|
|
||||||
|
ASTNode* node;
|
||||||
|
emitASTNodeLiteral(&node, literal);
|
||||||
|
freeLiteral(literal);
|
||||||
|
freeNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//test compound (dictionary)
|
||||||
|
char* idn = "foobar";
|
||||||
|
char* str = "hello world";
|
||||||
|
|
||||||
|
ASTNode* dictionary;
|
||||||
|
ASTNode* left;
|
||||||
|
ASTNode* right;
|
||||||
|
|
||||||
|
Literal identifier = TO_IDENTIFIER_LITERAL(copyString(idn, strlen(idn)), strlen(idn));
|
||||||
|
Literal string = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str));
|
||||||
|
|
||||||
|
emitASTNodeCompound(&dictionary, LITERAL_DICTIONARY);
|
||||||
|
emitASTNodeLiteral(&left, identifier);
|
||||||
|
emitASTNodeLiteral(&right, string);
|
||||||
|
|
||||||
|
//grow the node if needed
|
||||||
|
if (dictionary->compound.capacity < dictionary->compound.count + 1) {
|
||||||
|
int oldCapacity = dictionary->compound.capacity;
|
||||||
|
|
||||||
|
dictionary->compound.capacity = GROW_CAPACITY(oldCapacity);
|
||||||
|
dictionary->compound.nodes = GROW_ARRAY(ASTNode, dictionary->compound.nodes, oldCapacity, dictionary->compound.capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
//store the left and right in the node
|
||||||
|
setASTNodePair(&dictionary->compound.nodes[dictionary->compound.count++], left, right);
|
||||||
|
|
||||||
|
//the real test
|
||||||
|
freeNode(dictionary);
|
||||||
|
freeLiteral(identifier);
|
||||||
|
freeLiteral(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(NOTICE "All good\n" RESET);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
//supress the print output
|
||||||
|
static void noPrintFn(const char* output) {
|
||||||
|
//NO OP
|
||||||
|
}
|
||||||
|
|
||||||
|
//compilation functions
|
||||||
|
char* readFile(char* path, size_t* fileSize) {
|
||||||
|
FILE* file = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
*fileSize = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
char* buffer = (char*)malloc(*fileSize + 1);
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||||
|
|
||||||
|
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||||
|
|
||||||
|
if (bytesRead < *fileSize) {
|
||||||
|
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* compileString(char* source, size_t* size) {
|
||||||
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
Compiler compiler;
|
||||||
|
|
||||||
|
initLexer(&lexer, source);
|
||||||
|
initParser(&parser, &lexer);
|
||||||
|
initCompiler(&compiler);
|
||||||
|
|
||||||
|
//run the parser until the end of the source
|
||||||
|
ASTNode* node = scanParser(&parser);
|
||||||
|
while(node != NULL) {
|
||||||
|
//pack up and leave
|
||||||
|
if (node->type == AST_NODEERROR) {
|
||||||
|
printf(ERROR "error node detected\n" RESET);
|
||||||
|
freeNode(node);
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCompiler(&compiler, node);
|
||||||
|
freeNode(node);
|
||||||
|
node = scanParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the bytecode dump
|
||||||
|
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
//no lexer to clean up
|
||||||
|
|
||||||
|
//finally
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(char* msg) {
|
||||||
|
printf("%s", msg);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
char* source = readFile("scripts/call-from-host.toy", &size);
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
free((void*)source);
|
||||||
|
|
||||||
|
if (!tb) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interpreter interpreter;
|
||||||
|
initInterpreter(&interpreter);
|
||||||
|
runInterpreter(&interpreter, tb, size);
|
||||||
|
|
||||||
|
//test answer
|
||||||
|
{
|
||||||
|
interpreter.printOutput("Testing answer");
|
||||||
|
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
callFn(&interpreter, "answer", &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1) {
|
||||||
|
error("Returns has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 42) {
|
||||||
|
error("Returned value is incorrect\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
//test identity
|
||||||
|
{
|
||||||
|
interpreter.printOutput("Testing identity");
|
||||||
|
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
//push an argument
|
||||||
|
float pi = 3.14;
|
||||||
|
Literal arg = TO_FLOAT_LITERAL(pi);
|
||||||
|
pushLiteralArray(&arguments, arg);
|
||||||
|
|
||||||
|
callFn(&interpreter, "identity", &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1) {
|
||||||
|
error("Returns has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
float epsilon = 0.1; //because floats are evil
|
||||||
|
|
||||||
|
if (!IS_FLOAT(returns.literals[0]) || fabs(AS_FLOAT(returns.literals[0]) - pi) > epsilon) {
|
||||||
|
error("Returned value is incorrect\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
//test makeCounter (closures)
|
||||||
|
{
|
||||||
|
interpreter.printOutput("Testing makeCounter (closures)");
|
||||||
|
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
callFn(&interpreter, "makeCounter", &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1) {
|
||||||
|
error("Returns has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//grab the resulting literal
|
||||||
|
Literal counter = popLiteralArray(&returns);
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
|
||||||
|
//call counter repeatedly
|
||||||
|
{
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
callLiteralFn(&interpreter, counter, &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments (1) has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1) {
|
||||||
|
error("Returns (1) has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 1) {
|
||||||
|
error("Returned value (1) is incorrect\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
callLiteralFn(&interpreter, counter, &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments (2) has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1) {
|
||||||
|
error("Returns (2) has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 2) {
|
||||||
|
error("Returned value (2) is incorrect\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
callLiteralFn(&interpreter, counter, &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments (3) has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1) {
|
||||||
|
error("Returns (3) has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 3) {
|
||||||
|
error("Returned value (3) is incorrect\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteral(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
//test assertion failure
|
||||||
|
{
|
||||||
|
interpreter.printOutput("Testing assertion failure");
|
||||||
|
|
||||||
|
setInterpreterAssert(&interpreter, noPrintFn);
|
||||||
|
|
||||||
|
LiteralArray arguments;
|
||||||
|
initLiteralArray(&arguments);
|
||||||
|
LiteralArray returns;
|
||||||
|
initLiteralArray(&returns);
|
||||||
|
|
||||||
|
bool ret = callFn(&interpreter, "fail", &arguments, &returns);
|
||||||
|
|
||||||
|
//check the results
|
||||||
|
if (arguments.count != 0) {
|
||||||
|
error("Arguments has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returns.count != 1 || !IS_NULL(returns.literals[0])) {
|
||||||
|
error("Returns has the wrong number of members\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
error("Assertion gives the wrong return value\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
freeLiteralArray(&arguments);
|
||||||
|
freeLiteralArray(&returns);
|
||||||
|
}
|
||||||
|
|
||||||
|
//clean up
|
||||||
|
freeInterpreter(&interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(NOTICE "All good\n" RESET);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//IO functions
|
||||||
|
char* readFile(char* path, size_t* fileSize) {
|
||||||
|
FILE* file = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
*fileSize = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
char* buffer = (char*)malloc(*fileSize + 1);
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||||
|
|
||||||
|
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||||
|
|
||||||
|
if (bytesRead < *fileSize) {
|
||||||
|
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
//test init & free
|
||||||
|
Compiler compiler;
|
||||||
|
initCompiler(&compiler);
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//source
|
||||||
|
char* source = "print null;";
|
||||||
|
|
||||||
|
//test basic compilation & collation
|
||||||
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
Compiler compiler;
|
||||||
|
|
||||||
|
initLexer(&lexer, source);
|
||||||
|
initParser(&parser, &lexer);
|
||||||
|
initCompiler(&compiler);
|
||||||
|
|
||||||
|
ASTNode* node = scanParser(&parser);
|
||||||
|
|
||||||
|
//write
|
||||||
|
writeCompiler(&compiler, node);
|
||||||
|
|
||||||
|
//collate
|
||||||
|
int size = 0;
|
||||||
|
unsigned char* bytecode = collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
FREE_ARRAY(unsigned char, bytecode, size);
|
||||||
|
freeNode(node);
|
||||||
|
freeParser(&parser);
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//source
|
||||||
|
size_t sourceLength = 0;
|
||||||
|
char* source = readFile("scripts/sample_code.toy", &sourceLength);
|
||||||
|
|
||||||
|
//test basic compilation & collation
|
||||||
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
Compiler compiler;
|
||||||
|
|
||||||
|
initLexer(&lexer, source);
|
||||||
|
initParser(&parser, &lexer);
|
||||||
|
initCompiler(&compiler);
|
||||||
|
|
||||||
|
ASTNode* node = scanParser(&parser);
|
||||||
|
while (node != NULL) {
|
||||||
|
if (node->type == AST_NODEERROR) {
|
||||||
|
fprintf(stderr, ERROR "ERROR: Error node found" RESET);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//write
|
||||||
|
writeCompiler(&compiler, node);
|
||||||
|
freeNode(node);
|
||||||
|
|
||||||
|
node = scanParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
//collate
|
||||||
|
int size = 0;
|
||||||
|
unsigned char* bytecode = collateCompiler(&compiler, &size);
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
FREE_ARRAY(char, source, sourceLength);
|
||||||
|
FREE_ARRAY(unsigned char, bytecode, size);
|
||||||
|
freeParser(&parser);
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(NOTICE "All good\n" RESET);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||