Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f5e2a29eb | |||
| 8b5a991ed1 | |||
| 511c0280df | |||
| 34e90879d7 | |||
| 08a417a66a | |||
| 60c07d64ef | |||
| 0f4d474231 | |||
| a6fc01f7be | |||
| 9b68589d87 | |||
| 3f0deaeb8a | |||
| 5028f9a9f6 | |||
| 76437cb093 | |||
| 92bc0466eb | |||
| 6eecfc31d6 | |||
| f7bab4da10 | |||
| d95c6e6479 | |||
| 47e6983ad1 | |||
| 66e8c0fa7a | |||
| 2ea552fedd | |||
| 3747d0a326 | |||
| c2a13e4183 | |||
| 062f676f52 | |||
| f5ebbdf847 | |||
| 1a3d08958b | |||
| b1a08289e2 | |||
| f1c2e449ea | |||
| a95ed58cd8 | |||
| 74a898aa47 | |||
| 32601ae9a9 | |||
| 8610ac2529 | |||
| 782f60da9f | |||
| 3a25df60e3 | |||
| 7d35c5b325 | |||
| 1e432387c2 | |||
| 42dbe038cf | |||
| 9563185ef1 | |||
| 1db834f721 | |||
| f39d725001 | |||
| 98ccfb4215 | |||
| 9d26f94e25 | |||
| ba4d4fedc1 | |||
| b2825690ec | |||
| 46d5d7245a | |||
| b0194db1c4 | |||
| dc8845fd0e | |||
| 2c4324db70 | |||
| 28b5c2dab0 | |||
| ebdc4c6cff | |||
| 151ad7656d | |||
| 94fabf2f4e | |||
| 655827f672 |
@@ -1,5 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: krgamestudios
|
||||
ko_fi: krgamestudios
|
||||
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Comprehensive Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "dev" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
test-valgrind:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install valgrind
|
||||
run: sudo apt install valgrind
|
||||
- name: make test (valgrind)
|
||||
run: make test
|
||||
|
||||
test-sanitized:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: make test (sanitized)
|
||||
run: make test-sanitized
|
||||
@@ -1,31 +0,0 @@
|
||||
#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
|
||||
@@ -1,13 +0,0 @@
|
||||
# License
|
||||
|
||||
Copyright (c) 2020-2023 Kayne Ruse, KR Game Studios
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -1,76 +1,4 @@
|
||||
<p align="center">
|
||||
<image src="toylogo.png" />
|
||||
</p>
|
||||
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).
|
||||
|
||||
# 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 external libraries
|
||||
* Fancy slice notation for strings, arrays and dictionaries
|
||||
* Can re-direct output, error and assertion failure messages
|
||||
* Open source under the zlib license
|
||||
|
||||
## Building
|
||||
|
||||
For Windows, Linux and MacOS, simply run `make` in the root directory.
|
||||
|
||||
Note: For Linux, you may need to `cd` into the `out` directory before running.
|
||||
|
||||
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)).
|
||||
|
||||
# Patrons via Patreon
|
||||
|
||||
* Seth A. Robinson
|
||||
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.
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
title: The Toy Programming Language
|
||||
description: Documentation For The Toy Programming Language
|
||||
keywords: programming,coding
|
||||
author: Kayne Ruse (Ratstail91)
|
||||
@@ -0,0 +1,8 @@
|
||||
<!-- 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>
|
||||
@@ -0,0 +1,21 @@
|
||||
<!-- 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">
|
||||
@@ -0,0 +1,21 @@
|
||||
<!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>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 463 B |
|
After Width: | Height: | Size: 454 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,18 @@
|
||||
---
|
||||
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).
|
||||
@@ -1,88 +0,0 @@
|
||||
# Optimisation Options
|
||||
# export CFLAGS+=-O2 -mtune=native -march=native
|
||||
# export CFLAGS+=-fsanitize=address,undefined
|
||||
|
||||
export TOY_OUTDIR = out
|
||||
|
||||
all: $(TOY_OUTDIR) repl
|
||||
|
||||
#repl builds
|
||||
repl: $(TOY_OUTDIR) library
|
||||
$(MAKE) -C repl
|
||||
|
||||
repl-static: $(TOY_OUTDIR) static
|
||||
$(MAKE) -C repl
|
||||
|
||||
repl-release: clean $(TOY_OUTDIR) library-release
|
||||
$(MAKE) -C repl release
|
||||
|
||||
repl-static-release: clean $(TOY_OUTDIR) static-release
|
||||
$(MAKE) -C repl release
|
||||
|
||||
#lib builds
|
||||
library: $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source library
|
||||
|
||||
static: $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source static
|
||||
|
||||
library-release: $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source library-release
|
||||
|
||||
static-release: $(TOY_OUTDIR)
|
||||
$(MAKE) -j8 -C source static-release
|
||||
|
||||
#utils
|
||||
test: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -C test
|
||||
|
||||
test-sanitized: export CFLAGS+=-fsanitize=address,undefined
|
||||
test-sanitized: export LIBS+=-static-libasan
|
||||
test-sanitized: export DISABLE_VALGRIND=true
|
||||
test-sanitized: clean $(TOY_OUTDIR)
|
||||
$(MAKE) -C test
|
||||
|
||||
$(TOY_OUTDIR):
|
||||
mkdir $(TOY_OUTDIR)
|
||||
|
||||
#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
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
@@ -1,613 +0,0 @@
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
#include "repl_tools.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct Toy_Runner {
|
||||
Toy_Interpreter interpreter;
|
||||
unsigned char* bytecode;
|
||||
size_t size;
|
||||
|
||||
bool dirty;
|
||||
} Toy_Runner;
|
||||
|
||||
//Toy native functions
|
||||
static int nativeLoadScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to loadScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the argument
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
int driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to loadScript\n");
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
driveLength++;
|
||||
}
|
||||
|
||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||
|
||||
//get the real drive file path
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
|
||||
|
||||
if (!TOY_IS_STRING(realDriveLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\n");
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
|
||||
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
|
||||
|
||||
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
|
||||
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
|
||||
|
||||
//clean up the drivepath stuff
|
||||
Toy_deleteRefString(realDrive);
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//check for file extensions
|
||||
if (!(filePath[realLength - 5] == '.' && filePath[realLength - 4] == 't' && filePath[realLength - 3] == 'o' && filePath[realLength - 2] == 'y')) {
|
||||
interpreter->errorOutput("Bad script file extension (expected .toy)\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check for break-out attempts
|
||||
for (int i = 0; i < realLength - 1; i++) {
|
||||
if (filePath[i] == '.' && filePath[i + 1] == '.') {
|
||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//load and compile the bytecode
|
||||
size_t fileSize = 0;
|
||||
char* source = Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!source) {
|
||||
interpreter->errorOutput("Failed to load source file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* bytecode = Toy_compileString(source, &fileSize);
|
||||
free((void*)source);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to compile source file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//build the runner object
|
||||
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
|
||||
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
|
||||
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
|
||||
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
|
||||
runner->interpreter.hooks = interpreter->hooks;
|
||||
runner->interpreter.scope = NULL;
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->bytecode = bytecode;
|
||||
runner->size = fileSize;
|
||||
runner->dirty = false;
|
||||
|
||||
//build the opaque object, and push it to the stack
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeLoadScriptBytecode(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to loadScriptBytecode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the argument
|
||||
Toy_Literal drivePathLiteral = Toy_popLiteralArray(arguments);
|
||||
Toy_RefString* drivePath = Toy_copyRefString(TOY_AS_STRING(drivePathLiteral));
|
||||
|
||||
//get the drive and path as a string (can't trust that pesky strtok - custom split) TODO: move this to refstring library
|
||||
int driveLength = 0;
|
||||
while (Toy_toCString(drivePath)[driveLength] != ':') {
|
||||
if (driveLength >= Toy_lengthRefString(drivePath)) {
|
||||
interpreter->errorOutput("Incorrect drive path format given to loadScriptBytecode\n");
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
driveLength++;
|
||||
}
|
||||
|
||||
Toy_RefString* drive = Toy_createRefStringLength(Toy_toCString(drivePath), driveLength);
|
||||
Toy_RefString* path = Toy_createRefStringLength( &Toy_toCString(drivePath)[driveLength + 1], Toy_lengthRefString(drivePath) - driveLength );
|
||||
|
||||
//get the real drive file path
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(drive); //NOTE: driveLiteral takes ownership of the refString
|
||||
Toy_Literal realDriveLiteral = Toy_getLiteralDictionary(Toy_getDriveDictionary(), driveLiteral);
|
||||
|
||||
if (!TOY_IS_STRING(realDriveLiteral)) {
|
||||
interpreter->errorOutput("Incorrect literal type found for drive: ");
|
||||
Toy_printLiteralCustom(realDriveLiteral, interpreter->errorOutput);
|
||||
interpreter->errorOutput("\n");
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the final real file path (concat) TODO: move this concat to refstring library
|
||||
Toy_RefString* realDrive = Toy_copyRefString(TOY_AS_STRING(realDriveLiteral));
|
||||
int realLength = Toy_lengthRefString(realDrive) + Toy_lengthRefString(path);
|
||||
|
||||
char* filePath = TOY_ALLOCATE(char, realLength + 1); //+1 for null
|
||||
snprintf(filePath, realLength, "%s%s", Toy_toCString(realDrive), Toy_toCString(path));
|
||||
|
||||
//clean up the drivepath stuff
|
||||
Toy_deleteRefString(realDrive);
|
||||
Toy_freeLiteral(realDriveLiteral);
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_deleteRefString(path);
|
||||
Toy_deleteRefString(drivePath);
|
||||
Toy_freeLiteral(drivePathLiteral);
|
||||
|
||||
//check for file extensions
|
||||
if (!(filePath[realLength - 4] == '.' && filePath[realLength - 3] == 't' && filePath[realLength - 2] == 'b')) {
|
||||
interpreter->errorOutput("Bad binary file extension (expected .tb)\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check for break-out attempts
|
||||
for (int i = 0; i < realLength - 1; i++) {
|
||||
if (filePath[i] == '.' && filePath[i + 1] == '.') {
|
||||
interpreter->errorOutput("Parent directory access not allowed\n");
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//load the bytecode
|
||||
size_t fileSize = 0;
|
||||
unsigned char* bytecode = (unsigned char*)Toy_readFile(filePath, &fileSize);
|
||||
|
||||
if (!bytecode) {
|
||||
interpreter->errorOutput("Failed to load bytecode file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//build the runner object
|
||||
Toy_Runner* runner = TOY_ALLOCATE(Toy_Runner, 1);
|
||||
Toy_setInterpreterPrint(&runner->interpreter, interpreter->printOutput);
|
||||
Toy_setInterpreterAssert(&runner->interpreter, interpreter->assertOutput);
|
||||
Toy_setInterpreterError(&runner->interpreter, interpreter->errorOutput);
|
||||
runner->interpreter.hooks = interpreter->hooks;
|
||||
runner->interpreter.scope = NULL;
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->bytecode = bytecode;
|
||||
runner->size = fileSize;
|
||||
runner->dirty = false;
|
||||
|
||||
//build the opaque object, and push it to the stack
|
||||
Toy_Literal runnerLiteral = TOY_TO_OPAQUE_LITERAL(runner, TOY_OPAQUE_TAG_RUNNER);
|
||||
Toy_pushLiteralArray(&interpreter->stack, runnerLiteral);
|
||||
|
||||
TOY_FREE_ARRAY(char, filePath, realLength);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeRunScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//run
|
||||
if (runner->dirty) {
|
||||
interpreter->errorOutput("Can't re-run a dirty script (try resetting it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* bytecodeCopy = TOY_ALLOCATE(unsigned char, runner->size);
|
||||
memcpy(bytecodeCopy, runner->bytecode, runner->size); //need a COPY of the bytecode, because the interpreter eats it
|
||||
|
||||
Toy_runInterpreter(&runner->interpreter, bytecodeCopy, runner->size);
|
||||
runner->dirty = true;
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeGetScriptVar(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _getScriptVar\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal varName = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal varNameIdn = varName;
|
||||
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
|
||||
Toy_freeLiteral(varNameIdn);
|
||||
}
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//dirty check
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't access variable from a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the desired variable
|
||||
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &result);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCallScriptFn(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count < 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _callScriptFn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the rest args
|
||||
Toy_LiteralArray tmp;
|
||||
Toy_initLiteralArray(&tmp);
|
||||
|
||||
while (arguments->count > 2) {
|
||||
Toy_Literal lit = Toy_popLiteralArray(arguments);
|
||||
Toy_pushLiteralArray(&tmp, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
Toy_LiteralArray rest;
|
||||
Toy_initLiteralArray(&rest);
|
||||
|
||||
while (tmp.count) { //correct the order of the rest args
|
||||
Toy_Literal lit = Toy_popLiteralArray(&tmp);
|
||||
Toy_pushLiteralArray(&rest, lit);
|
||||
Toy_freeLiteral(lit);
|
||||
}
|
||||
|
||||
Toy_freeLiteralArray(&tmp);
|
||||
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal varName = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal varNameIdn = varName;
|
||||
if (TOY_IS_IDENTIFIER(varName) && Toy_parseIdentifierToValue(interpreter, &varName)) {
|
||||
Toy_freeLiteral(varNameIdn);
|
||||
}
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//dirty check
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't access fn from a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the desired variable
|
||||
Toy_Literal varIdn = TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_STRING(varName)));
|
||||
Toy_Literal fn = TOY_TO_NULL_LITERAL;
|
||||
Toy_getScopeVariable(runner->interpreter.scope, varIdn, &fn);
|
||||
|
||||
if (!TOY_IS_FUNCTION(fn)) {
|
||||
interpreter->errorOutput("Can't run a non-function literal\n");
|
||||
Toy_freeLiteral(fn);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
}
|
||||
|
||||
//call
|
||||
Toy_LiteralArray resultArray;
|
||||
Toy_initLiteralArray(&resultArray);
|
||||
|
||||
Toy_callLiteralFn(interpreter, fn, &rest, &resultArray);
|
||||
|
||||
Toy_Literal result = TOY_TO_NULL_LITERAL;
|
||||
if (resultArray.count > 0) {
|
||||
result = Toy_popLiteralArray(&resultArray);
|
||||
}
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteralArray(&resultArray);
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(fn);
|
||||
Toy_freeLiteral(varIdn);
|
||||
Toy_freeLiteral(varName);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
Toy_freeLiteralArray(&rest);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeResetScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _resetScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//reset
|
||||
if (!runner->dirty) {
|
||||
interpreter->errorOutput("Can't reset a non-dirty script (try running it first)\n");
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_resetInterpreter(&runner->interpreter);
|
||||
runner->dirty = false;
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeFreeScript(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _freeScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in _freeScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//clear out the runner object
|
||||
runner->interpreter.hooks = NULL;
|
||||
Toy_freeInterpreter(&runner->interpreter);
|
||||
TOY_FREE_ARRAY(unsigned char, runner->bytecode, runner->size);
|
||||
|
||||
TOY_FREE(Toy_Runner, runner);
|
||||
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nativeCheckScriptDirty(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the runner object
|
||||
Toy_Literal runnerLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal runnerIdn = runnerLiteral;
|
||||
if (TOY_IS_IDENTIFIER(runnerLiteral) && Toy_parseIdentifierToValue(interpreter, &runnerLiteral)) {
|
||||
Toy_freeLiteral(runnerIdn);
|
||||
}
|
||||
|
||||
if (TOY_GET_OPAQUE_TAG(runnerLiteral) != TOY_OPAQUE_TAG_RUNNER) {
|
||||
interpreter->errorOutput("Unrecognized opaque literal in _runScript\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Toy_Runner* runner = TOY_AS_OPAQUE(runnerLiteral);
|
||||
|
||||
//run
|
||||
Toy_Literal result = TOY_TO_BOOLEAN_LITERAL(runner->dirty);
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(result);
|
||||
Toy_freeLiteral(runnerLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"loadScript", nativeLoadScript},
|
||||
{"loadScriptBytecode", nativeLoadScriptBytecode},
|
||||
{"_runScript", nativeRunScript},
|
||||
{"_getScriptVar", nativeGetScriptVar},
|
||||
{"_callScriptFn", nativeCallScriptFn},
|
||||
{"_resetScript", nativeResetScript},
|
||||
{"_freeScript", nativeFreeScript},
|
||||
{"_checkScriptDirty", nativeCheckScriptDirty},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return false;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = TOY_LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//file system API
|
||||
static Toy_LiteralDictionary Toy_driveDictionary;
|
||||
|
||||
void Toy_initDriveDictionary() {
|
||||
Toy_initLiteralDictionary(&Toy_driveDictionary);
|
||||
}
|
||||
|
||||
void Toy_freeDriveDictionary() {
|
||||
Toy_freeLiteralDictionary(&Toy_driveDictionary);
|
||||
}
|
||||
|
||||
Toy_LiteralDictionary* Toy_getDriveDictionary() {
|
||||
return &Toy_driveDictionary;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookRunner(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
//file system API - these need to be set by the host
|
||||
void Toy_initDriveDictionary();
|
||||
void Toy_freeDriveDictionary();
|
||||
Toy_LiteralDictionary* Toy_getDriveDictionary();
|
||||
|
||||
#define TOY_OPAQUE_TAG_RUNNER 100
|
||||
@@ -1,95 +0,0 @@
|
||||
#include "lib_standard.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static int nativeClock(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 0) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to clock\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the time from C (what a pain)
|
||||
time_t rawtime = time(NULL);
|
||||
struct tm* timeinfo = localtime( &rawtime );
|
||||
char* timestr = asctime(timeinfo);
|
||||
|
||||
//push to the stack
|
||||
int len = strlen(timestr) - 1; //-1 for the newline
|
||||
Toy_Literal timeLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(timestr, len));
|
||||
|
||||
//push to the stack
|
||||
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"clock", nativeClock},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return false;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = TOY_LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
@@ -1,412 +0,0 @@
|
||||
#include "lib_timer.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
|
||||
static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
|
||||
//normallize
|
||||
if (x->tv_usec > 999999) {
|
||||
x->tv_sec += x->tv_usec / 1000000;
|
||||
x->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
if (y->tv_usec > 999999) {
|
||||
y->tv_sec += y->tv_usec / 1000000;
|
||||
y->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
//calc
|
||||
result->tv_sec = x->tv_sec - y->tv_sec;
|
||||
|
||||
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
|
||||
if (result->tv_sec != 0) { //only works far from 0
|
||||
result->tv_usec += 1000000;
|
||||
result->tv_sec--; // borrow
|
||||
}
|
||||
}
|
||||
|
||||
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
|
||||
}
|
||||
|
||||
//god damn it
|
||||
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
|
||||
struct timeval* d = TOY_ALLOCATE(struct timeval, 1);
|
||||
|
||||
//I gave up, copied from SO
|
||||
timeval_subtract(d, rhs, lhs);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//callbacks
|
||||
static int nativeStartTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 0) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the timeinfo from C
|
||||
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
|
||||
gettimeofday(timeinfo, NULL);
|
||||
|
||||
//wrap in an opaque literal for Toy
|
||||
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeStopTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the timeinfo from C
|
||||
struct timeval timerStop;
|
||||
gettimeofday(&timerStop, NULL);
|
||||
|
||||
//unwrap the opaque literal
|
||||
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal timeLiteralIdn = timeLiteral;
|
||||
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
Toy_freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timerStart = TOY_AS_OPAQUE(timeLiteral);
|
||||
|
||||
//determine the difference, and wrap it
|
||||
struct timeval* d = diff(timerStart, &timerStop);
|
||||
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
|
||||
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
Toy_freeLiteral(diffLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCreateTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the args
|
||||
Toy_Literal microsecondLiteral = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal secondLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal secondLiteralIdn = secondLiteral;
|
||||
if (TOY_IS_IDENTIFIER(secondLiteral) && Toy_parseIdentifierToValue(interpreter, &secondLiteral)) {
|
||||
Toy_freeLiteral(secondLiteralIdn);
|
||||
}
|
||||
|
||||
Toy_Literal microsecondLiteralIdn = microsecondLiteral;
|
||||
if (TOY_IS_IDENTIFIER(microsecondLiteral) && Toy_parseIdentifierToValue(interpreter, µsecondLiteral)) {
|
||||
Toy_freeLiteral(microsecondLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_INTEGER(secondLiteral) || !TOY_IS_INTEGER(microsecondLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
|
||||
Toy_freeLiteral(secondLiteral);
|
||||
Toy_freeLiteral(microsecondLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TOY_AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || TOY_AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (TOY_AS_INTEGER(secondLiteral) != 0 && TOY_AS_INTEGER(microsecondLiteral) < 0) ) {
|
||||
interpreter->errorOutput("Microseconds out of range in createTimer\n");
|
||||
Toy_freeLiteral(secondLiteral);
|
||||
Toy_freeLiteral(microsecondLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the timeinfo from toy
|
||||
struct timeval* timeinfo = TOY_ALLOCATE(struct timeval, 1);
|
||||
timeinfo->tv_sec = TOY_AS_INTEGER(secondLiteral);
|
||||
timeinfo->tv_usec = TOY_AS_INTEGER(microsecondLiteral);
|
||||
|
||||
//wrap in an opaque literal for Toy
|
||||
Toy_Literal timeLiteral = TOY_TO_OPAQUE_LITERAL(timeinfo, -1);
|
||||
Toy_pushLiteralArray(&interpreter->stack, timeLiteral);
|
||||
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
Toy_freeLiteral(secondLiteral);
|
||||
Toy_freeLiteral(microsecondLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeGetTimerSeconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap the opaque literal
|
||||
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal timeLiteralIdn = timeLiteral;
|
||||
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
Toy_freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
|
||||
|
||||
//create the result literal
|
||||
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_sec);
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
Toy_freeLiteral(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeGetTimerMicroseconds(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap the opaque literal
|
||||
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal timeLiteralIdn = timeLiteral;
|
||||
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
Toy_freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
|
||||
|
||||
//create the result literal
|
||||
Toy_Literal result = TOY_TO_INTEGER_LITERAL(timer->tv_usec);
|
||||
Toy_pushLiteralArray(&interpreter->stack, result);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
Toy_freeLiteral(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeCompareTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 2) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap the opaque literals
|
||||
Toy_Literal rhsLiteral = Toy_popLiteralArray(arguments);
|
||||
Toy_Literal lhsLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal lhsLiteralIdn = lhsLiteral;
|
||||
if (TOY_IS_IDENTIFIER(lhsLiteral) && Toy_parseIdentifierToValue(interpreter, &lhsLiteral)) {
|
||||
Toy_freeLiteral(lhsLiteralIdn);
|
||||
}
|
||||
|
||||
Toy_Literal rhsLiteralIdn = rhsLiteral;
|
||||
if (TOY_IS_IDENTIFIER(rhsLiteral) && Toy_parseIdentifierToValue(interpreter, &rhsLiteral)) {
|
||||
Toy_freeLiteral(rhsLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_OPAQUE(lhsLiteral) || !TOY_IS_OPAQUE(rhsLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
|
||||
Toy_freeLiteral(lhsLiteral);
|
||||
Toy_freeLiteral(rhsLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* lhsTimer = TOY_AS_OPAQUE(lhsLiteral);
|
||||
struct timeval* rhsTimer = TOY_AS_OPAQUE(rhsLiteral);
|
||||
|
||||
//determine the difference, and wrap it
|
||||
struct timeval* d = diff(lhsTimer, rhsTimer);
|
||||
Toy_Literal diffLiteral = TOY_TO_OPAQUE_LITERAL(d, -1);
|
||||
Toy_pushLiteralArray(&interpreter->stack, diffLiteral);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(lhsLiteral);
|
||||
Toy_freeLiteral(rhsLiteral);
|
||||
Toy_freeLiteral(diffLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeTimerToString(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap in an opaque literal
|
||||
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal timeLiteralIdn = timeLiteral;
|
||||
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
Toy_freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
|
||||
|
||||
//create the string literal
|
||||
Toy_Literal resultLiteral = TOY_TO_NULL_LITERAL;
|
||||
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
|
||||
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
|
||||
}
|
||||
else { //normal case
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
|
||||
resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strlen(buffer)));
|
||||
}
|
||||
|
||||
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
Toy_freeLiteral(resultLiteral);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nativeDestroyTimer(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||
//no arguments
|
||||
if (arguments->count != 1) {
|
||||
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//unwrap in an opaque literal
|
||||
Toy_Literal timeLiteral = Toy_popLiteralArray(arguments);
|
||||
|
||||
Toy_Literal timeLiteralIdn = timeLiteral;
|
||||
if (TOY_IS_IDENTIFIER(timeLiteral) && Toy_parseIdentifierToValue(interpreter, &timeLiteral)) {
|
||||
Toy_freeLiteral(timeLiteralIdn);
|
||||
}
|
||||
|
||||
if (!TOY_IS_OPAQUE(timeLiteral)) {
|
||||
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval* timer = TOY_AS_OPAQUE(timeLiteral);
|
||||
|
||||
TOY_FREE(struct timeval, timer);
|
||||
|
||||
Toy_freeLiteral(timeLiteral);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//call the hook
|
||||
typedef struct Natives {
|
||||
char* name;
|
||||
Toy_NativeFn fn;
|
||||
} Natives;
|
||||
|
||||
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||
//build the natives list
|
||||
Natives natives[] = {
|
||||
{"startTimer", nativeStartTimer},
|
||||
{"_stopTimer", nativeStopTimer},
|
||||
{"createTimer", nativeCreateTimer},
|
||||
{"_getTimerSeconds", nativeGetTimerSeconds},
|
||||
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
|
||||
{"_compareTimer", nativeCompareTimer},
|
||||
{"_timerToString", nativeTimerToString},
|
||||
{"_destroyTimer", nativeDestroyTimer},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
//store the library in an aliased dictionary
|
||||
if (!TOY_IS_NULL(alias)) {
|
||||
//make sure the name isn't taken
|
||||
if (Toy_isDelcaredScopeVariable(interpreter->scope, alias)) {
|
||||
interpreter->errorOutput("Can't override an existing variable\n");
|
||||
Toy_freeLiteral(alias);
|
||||
return false;
|
||||
}
|
||||
|
||||
//create the dictionary to load up with functions
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//load the dict with functions
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_Literal name = TOY_TO_STRING_LITERAL(Toy_createRefString(natives[i].name));
|
||||
Toy_Literal func = TOY_TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
|
||||
func.type = TOY_LITERAL_FUNCTION_NATIVE;
|
||||
|
||||
Toy_setLiteralDictionary(dictionary, name, func);
|
||||
|
||||
Toy_freeLiteral(name);
|
||||
Toy_freeLiteral(func);
|
||||
}
|
||||
|
||||
//build the type
|
||||
Toy_Literal type = TOY_TO_TYPE_LITERAL(TOY_LITERAL_DICTIONARY, true);
|
||||
Toy_Literal strType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_STRING, true);
|
||||
Toy_Literal fnType = TOY_TO_TYPE_LITERAL(TOY_LITERAL_FUNCTION_NATIVE, true);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, strType);
|
||||
TOY_TYPE_PUSH_SUBTYPE(&type, fnType);
|
||||
|
||||
//set scope
|
||||
Toy_Literal dict = TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
Toy_declareScopeVariable(interpreter->scope, alias, type);
|
||||
Toy_setScopeVariable(interpreter->scope, alias, dict, false);
|
||||
|
||||
//cleanup
|
||||
Toy_freeLiteral(dict);
|
||||
Toy_freeLiteral(type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//default
|
||||
for (int i = 0; natives[i].name; i++) {
|
||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
int Toy_hookTimer(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
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
|
||||
|
||||
release: all
|
||||
strip $(OUT)
|
||||
|
||||
$(OBJ): | $(ODIR)
|
||||
|
||||
$(ODIR):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) $(ODIR)
|
||||
rm /usr/local/lib/lib$(OUTNAME).dylib
|
||||
@@ -1,173 +0,0 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_timer.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_compiler.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void repl() {
|
||||
//repl does it's own thing for now
|
||||
bool error = false;
|
||||
|
||||
const int size = 2048;
|
||||
char input[size];
|
||||
memset(input, 0, size);
|
||||
|
||||
Toy_Interpreter interpreter; //persist the interpreter for the scopes
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
for(;;) {
|
||||
printf("> ");
|
||||
|
||||
//handle EOF for exits
|
||||
if (!fgets(input, size, stdin)) {
|
||||
break;
|
||||
}
|
||||
|
||||
//escape the repl (length of 5 to accomodate the newline)
|
||||
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
|
||||
break;
|
||||
}
|
||||
|
||||
//setup this iteration
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
|
||||
Toy_initLexer(&lexer, input);
|
||||
Toy_initParser(&parser, &lexer);
|
||||
Toy_initCompiler(&compiler);
|
||||
|
||||
//run this iteration
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//pack up and restart
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
printf(TOY_CC_ERROR "error node detected\n" TOY_CC_RESET);
|
||||
error = true;
|
||||
Toy_freeASTNode(node);
|
||||
break;
|
||||
}
|
||||
|
||||
Toy_writeCompiler(&compiler, node);
|
||||
Toy_freeASTNode(node);
|
||||
node = Toy_scanParser(&parser);
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
//get the bytecode dump
|
||||
int size = 0;
|
||||
unsigned char* tb = Toy_collateCompiler(&compiler, &size);
|
||||
|
||||
//run the bytecode
|
||||
Toy_runInterpreter(&interpreter, tb, size);
|
||||
}
|
||||
|
||||
//clean up this iteration
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
error = false;
|
||||
}
|
||||
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
//entry point
|
||||
int main(int argc, const char* argv[]) {
|
||||
Toy_initCommand(argc, argv);
|
||||
|
||||
//lib setup (hacky - only really for this program)
|
||||
Toy_initDriveDictionary();
|
||||
|
||||
Toy_Literal driveLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
||||
Toy_Literal pathLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("scripts"));
|
||||
|
||||
Toy_setLiteralDictionary(Toy_getDriveDictionary(), driveLiteral, pathLiteral);
|
||||
|
||||
Toy_freeLiteral(driveLiteral);
|
||||
Toy_freeLiteral(pathLiteral);
|
||||
|
||||
//command specific actions
|
||||
if (command.error) {
|
||||
Toy_usageCommand(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.help) {
|
||||
Toy_helpCommand(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.version) {
|
||||
Toy_copyrightCommand(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//version
|
||||
if (command.verbose) {
|
||||
printf(TOY_CC_NOTICE "Toy Programming Language Version %d.%d.%d\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||
}
|
||||
|
||||
//run source file
|
||||
if (command.sourcefile) {
|
||||
Toy_runSourceFile(command.sourcefile);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run from stdin
|
||||
if (command.source) {
|
||||
Toy_runSource(command.source);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//compile source file
|
||||
if (command.compilefile && command.outfile) {
|
||||
size_t size = 0;
|
||||
char* source = Toy_readFile(command.compilefile, &size);
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return 1;
|
||||
}
|
||||
Toy_writeFile(command.outfile, tb, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//run binary
|
||||
if (command.binaryfile) {
|
||||
Toy_runBinaryFile(command.binaryfile);
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
repl();
|
||||
|
||||
//lib cleanup
|
||||
Toy_freeDriveDictionary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
#include "repl_tools.h"
|
||||
#include "lib_standard.h"
|
||||
#include "lib_timer.h"
|
||||
#include "lib_runner.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_compiler.h"
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//IO functions
|
||||
char* Toy_readFile(char* path, size_t* fileSize) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(file, 0L, SEEK_END);
|
||||
*fileSize = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* buffer = (char*)malloc(*fileSize + 1);
|
||||
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Not enough memory to read \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||
|
||||
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||
|
||||
if (bytesRead < *fileSize) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not read file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int Toy_writeFile(char* path, unsigned char* bytes, size_t size) {
|
||||
FILE* file = fopen(path, "wb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not open file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int written = fwrite(bytes, size, 1, file);
|
||||
|
||||
if (written != 1) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Could not write file \"%s\"\n" TOY_CC_RESET, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//repl functions
|
||||
unsigned char* Toy_compileString(char* source, size_t* size) {
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
Toy_Compiler compiler;
|
||||
|
||||
Toy_initLexer(&lexer, source);
|
||||
Toy_initParser(&parser, &lexer);
|
||||
Toy_initCompiler(&compiler);
|
||||
|
||||
//run the parser until the end of the source
|
||||
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
//pack up and leave
|
||||
if (node->type == TOY_AST_NODE_ERROR) {
|
||||
Toy_freeASTNode(node);
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_writeCompiler(&compiler, node);
|
||||
Toy_freeASTNode(node);
|
||||
node = Toy_scanParser(&parser);
|
||||
}
|
||||
|
||||
//get the bytecode dump
|
||||
unsigned char* tb = Toy_collateCompiler(&compiler, (int*)(size));
|
||||
|
||||
//cleanup
|
||||
Toy_freeCompiler(&compiler);
|
||||
Toy_freeParser(&parser);
|
||||
//no lexer to clean up
|
||||
|
||||
//finally
|
||||
return tb;
|
||||
}
|
||||
|
||||
void Toy_runBinary(unsigned char* tb, size_t size) {
|
||||
Toy_Interpreter interpreter;
|
||||
Toy_initInterpreter(&interpreter);
|
||||
|
||||
//inject the libs
|
||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||
Toy_injectNativeHook(&interpreter, "timer", Toy_hookTimer);
|
||||
Toy_injectNativeHook(&interpreter, "runner", Toy_hookRunner);
|
||||
|
||||
Toy_runInterpreter(&interpreter, tb, size);
|
||||
Toy_freeInterpreter(&interpreter);
|
||||
}
|
||||
|
||||
void Toy_runBinaryFile(char* fname) {
|
||||
size_t size = 0; //not used
|
||||
unsigned char* tb = (unsigned char*)Toy_readFile(fname, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
Toy_runBinary(tb, size);
|
||||
//interpreter takes ownership of the binary data
|
||||
}
|
||||
|
||||
void Toy_runSource(char* source) {
|
||||
size_t size = 0;
|
||||
unsigned char* tb = Toy_compileString(source, &size);
|
||||
if (!tb) {
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_runBinary(tb, size);
|
||||
}
|
||||
|
||||
void Toy_runSourceFile(char* fname) {
|
||||
size_t size = 0; //not used
|
||||
char* source = Toy_readFile(fname, &size);
|
||||
Toy_runSource(source);
|
||||
free((void*)source);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
char* Toy_readFile(char* path, size_t* fileSize);
|
||||
int Toy_writeFile(char* path, unsigned char* bytes, size_t size);
|
||||
|
||||
unsigned char* Toy_compileString(char* source, size_t* size);
|
||||
|
||||
void Toy_runBinary(unsigned char* tb, size_t size);
|
||||
void Toy_runBinaryFile(char* fname);
|
||||
void Toy_runSource(char* source);
|
||||
void Toy_runSourceFile(char* fname);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 730 B |
@@ -1,89 +0,0 @@
|
||||
//single line comment
|
||||
|
||||
/*
|
||||
multi line comment
|
||||
*/
|
||||
|
||||
//test primitive literals
|
||||
print "hello world";
|
||||
print null;
|
||||
print true;
|
||||
print false;
|
||||
print 42;
|
||||
print 3.14;
|
||||
print -69;
|
||||
print -4.20;
|
||||
print 2 + (3 * 3);
|
||||
|
||||
//test operators (integers)
|
||||
print 1 + 1;
|
||||
print 1 - 1;
|
||||
print 2 * 2;
|
||||
print 1 / 2;
|
||||
print 4 % 2;
|
||||
|
||||
//test operators (floats)
|
||||
print 1.0 + 1.0;
|
||||
print 1.0 - 1.0;
|
||||
print 2.0 * 2.0;
|
||||
print 1.0 / 2.0;
|
||||
|
||||
//test scopes
|
||||
{
|
||||
print "This statement is within a scope.";
|
||||
{
|
||||
print "This is a deeper scope.";
|
||||
}
|
||||
}
|
||||
print "Back to the outer scope.";
|
||||
|
||||
//test scope will delegate to higher scope
|
||||
var a = 1;
|
||||
{
|
||||
a = 2;
|
||||
print a;
|
||||
}
|
||||
print a;
|
||||
|
||||
//test scope will shadow higher scope on redefine
|
||||
var b: int = 3;
|
||||
{
|
||||
var b = 4;
|
||||
print b;
|
||||
}
|
||||
print b;
|
||||
|
||||
//test compounds, repeatedly
|
||||
print [1, 2, 3];
|
||||
print [4, 5];
|
||||
print ["key":"value"];
|
||||
print [1, 2, 3];
|
||||
print [4, 5];
|
||||
print ["key":"value"];
|
||||
|
||||
//test empties
|
||||
print [];
|
||||
print [:];
|
||||
|
||||
//test nested compounds
|
||||
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
||||
|
||||
//var declarations
|
||||
var x = 31;
|
||||
var y : int = 42;
|
||||
var arr : [int] = [1, 2, 3, 42];
|
||||
var dict : [string:int] = ["hello": 1, "world":2];
|
||||
|
||||
//printing expressions
|
||||
print x;
|
||||
print x + y;
|
||||
print arr;
|
||||
print dict;
|
||||
|
||||
//test asserts at the end of the file
|
||||
assert x, "This won't be seen";
|
||||
assert true, "This won't be seen";
|
||||
assert false, "This is a failed assert, and will end execution";
|
||||
|
||||
print "This will not be printed because of the above assert";
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
//number of iterations
|
||||
var SIZE: int const = 100;
|
||||
|
||||
//lookup table
|
||||
var lookup = [
|
||||
"*": [
|
||||
"*": [
|
||||
"*": " ",
|
||||
" ": "*"
|
||||
],
|
||||
" ": [
|
||||
"*": "*",
|
||||
" ": " "
|
||||
]
|
||||
], " ": [
|
||||
"*": [
|
||||
"*": "*",
|
||||
" ": "*"
|
||||
],
|
||||
" ": [
|
||||
"*": "*",
|
||||
" ": " "
|
||||
]
|
||||
]];
|
||||
|
||||
//initial line to build from
|
||||
var prev: string = "";
|
||||
for (var i = 0; i < SIZE -1; i++) {
|
||||
prev += " ";
|
||||
}
|
||||
prev += "*"; //initial
|
||||
print prev;
|
||||
|
||||
//run
|
||||
for (var iteration = 0; iteration < 100; iteration++) {
|
||||
//left
|
||||
var output = (lookup[" "][prev[0]][prev[1]]);
|
||||
|
||||
//middle
|
||||
for (var i = 1; i < SIZE-1; i++) {
|
||||
output += (lookup[prev[i-1]][prev[i]][prev[i+1]]);
|
||||
}
|
||||
|
||||
//right
|
||||
output += (lookup[prev[SIZE-2]][prev[SIZE-1]][" "]);
|
||||
|
||||
print output;
|
||||
prev = output;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//test basic truth ternaries
|
||||
{
|
||||
assert true ? true : false, "Basic true ternary failed";
|
||||
assert false ? false : true, "Basic false ternary failed";
|
||||
}
|
||||
|
||||
|
||||
//test nesting
|
||||
{
|
||||
fn least(a, b, c) {
|
||||
return a < b ? a : b < c ? b : c;
|
||||
}
|
||||
|
||||
assert least(1, 2, 3) == 1, "Least 1, 2, 3 failed";
|
||||
assert least(10, 5, 7) == 5, "Least 10, 5, 7 failed";
|
||||
assert least(9, 7, 5) == 5, "Least 9, 7, 5 failed";
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
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)
|
||||
|
||||
library-release: $(OBJ) library
|
||||
strip $(OUT)
|
||||
|
||||
static-release: $(OBJ) static
|
||||
strip -d ../$(TOY_OUTDIR)/lib$(OUTNAME).a
|
||||
|
||||
$(OBJ): | $(ODIR)
|
||||
|
||||
$(ODIR):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) $(ODIR)
|
||||
@@ -1,385 +0,0 @@
|
||||
#include "toy_ast_node.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void freeASTNodeCustom(Toy_ASTNode* node, bool freeSelf) {
|
||||
//don't free a NULL node
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(node->type) {
|
||||
case TOY_AST_NODE_ERROR:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_LITERAL:
|
||||
Toy_freeLiteral(node->atomic.literal);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_UNARY:
|
||||
Toy_freeASTNode(node->unary.child);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BINARY:
|
||||
Toy_freeASTNode(node->binary.left);
|
||||
Toy_freeASTNode(node->binary.right);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_TERNARY:
|
||||
Toy_freeASTNode(node->ternary.condition);
|
||||
Toy_freeASTNode(node->ternary.thenPath);
|
||||
Toy_freeASTNode(node->ternary.elsePath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_GROUPING:
|
||||
Toy_freeASTNode(node->grouping.child);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BLOCK:
|
||||
for (int i = 0; i < node->block.count; i++) {
|
||||
freeASTNodeCustom(node->block.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->block.nodes, node->block.capacity);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_COMPOUND:
|
||||
for (int i = 0; i < node->compound.count; i++) {
|
||||
freeASTNodeCustom(node->compound.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->compound.nodes, node->compound.capacity);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PAIR:
|
||||
Toy_freeASTNode(node->pair.left);
|
||||
Toy_freeASTNode(node->pair.right);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_INDEX:
|
||||
Toy_freeASTNode(node->index.first);
|
||||
Toy_freeASTNode(node->index.second);
|
||||
Toy_freeASTNode(node->index.third);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_VAR_DECL:
|
||||
Toy_freeLiteral(node->varDecl.identifier);
|
||||
Toy_freeLiteral(node->varDecl.typeLiteral);
|
||||
Toy_freeASTNode(node->varDecl.expression);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_COLLECTION:
|
||||
for (int i = 0; i < node->fnCollection.count; i++) {
|
||||
freeASTNodeCustom(node->fnCollection.nodes + i, false);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_DECL:
|
||||
Toy_freeLiteral(node->fnDecl.identifier);
|
||||
Toy_freeASTNode(node->fnDecl.arguments);
|
||||
Toy_freeASTNode(node->fnDecl.returns);
|
||||
Toy_freeASTNode(node->fnDecl.block);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_CALL:
|
||||
Toy_freeASTNode(node->fnCall.arguments);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FN_RETURN:
|
||||
Toy_freeASTNode(node->returns.returns);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_IF:
|
||||
Toy_freeASTNode(node->pathIf.condition);
|
||||
Toy_freeASTNode(node->pathIf.thenPath);
|
||||
Toy_freeASTNode(node->pathIf.elsePath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_WHILE:
|
||||
Toy_freeASTNode(node->pathWhile.condition);
|
||||
Toy_freeASTNode(node->pathWhile.thenPath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_FOR:
|
||||
Toy_freeASTNode(node->pathFor.preClause);
|
||||
Toy_freeASTNode(node->pathFor.postClause);
|
||||
Toy_freeASTNode(node->pathFor.condition);
|
||||
Toy_freeASTNode(node->pathFor.thenPath);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_BREAK:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_CONTINUE:
|
||||
//NO-OP
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_PREFIX_INCREMENT:
|
||||
Toy_freeLiteral(node->prefixIncrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_PREFIX_DECREMENT:
|
||||
Toy_freeLiteral(node->prefixDecrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_POSTFIX_INCREMENT:
|
||||
Toy_freeLiteral(node->postfixIncrement.identifier);
|
||||
break;
|
||||
case TOY_AST_NODE_POSTFIX_DECREMENT:
|
||||
Toy_freeLiteral(node->postfixDecrement.identifier);
|
||||
break;
|
||||
|
||||
case TOY_AST_NODE_IMPORT:
|
||||
Toy_freeLiteral(node->import.identifier);
|
||||
Toy_freeLiteral(node->import.alias);
|
||||
break;
|
||||
}
|
||||
|
||||
if (freeSelf) {
|
||||
TOY_FREE(Toy_ASTNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_freeASTNode(Toy_ASTNode* node) {
|
||||
freeASTNodeCustom(node, true);
|
||||
}
|
||||
|
||||
//various emitters
|
||||
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal) {
|
||||
//allocate a new node
|
||||
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = TOY_AST_NODE_LITERAL;
|
||||
(*nodeHandle)->atomic.literal = Toy_copyLiteral(literal);
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child) {
|
||||
//allocate a new node
|
||||
*nodeHandle = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
(*nodeHandle)->type = TOY_AST_NODE_UNARY;
|
||||
(*nodeHandle)->unary.opcode = opcode;
|
||||
(*nodeHandle)->unary.child = child;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BINARY;
|
||||
tmp->binary.opcode = opcode;
|
||||
tmp->binary.left = *nodeHandle;
|
||||
tmp->binary.right = rhs;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_TERNARY;
|
||||
tmp->ternary.condition = condition;
|
||||
tmp->ternary.thenPath = thenPath;
|
||||
tmp->ternary.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_GROUPING;
|
||||
tmp->grouping.child = *nodeHandle;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BLOCK;
|
||||
tmp->block.nodes = NULL; //NOTE: appended by the parser
|
||||
tmp->block.capacity = 0;
|
||||
tmp->block.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_COMPOUND;
|
||||
tmp->compound.literalType = literalType;
|
||||
tmp->compound.nodes = NULL;
|
||||
tmp->compound.capacity = 0;
|
||||
tmp->compound.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right) {
|
||||
//set - assume the node has already been allocated
|
||||
node->type = TOY_AST_NODE_PAIR;
|
||||
node->pair.left = left;
|
||||
node->pair.right = right;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_INDEX;
|
||||
tmp->index.first = first;
|
||||
tmp->index.second = second;
|
||||
tmp->index.third = third;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal typeLiteral, Toy_ASTNode* expression) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_VAR_DECL;
|
||||
tmp->varDecl.identifier = identifier;
|
||||
tmp->varDecl.typeLiteral = typeLiteral;
|
||||
tmp->varDecl.expression = expression;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_COLLECTION;
|
||||
tmp->fnCollection.nodes = NULL;
|
||||
tmp->fnCollection.capacity = 0;
|
||||
tmp->fnCollection.count = 0;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_DECL;
|
||||
tmp->fnDecl.identifier = identifier;
|
||||
tmp->fnDecl.arguments = arguments;
|
||||
tmp->fnDecl.returns = returns;
|
||||
tmp->fnDecl.block = block;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_CALL;
|
||||
tmp->fnCall.arguments = arguments;
|
||||
tmp->fnCall.argumentCount = arguments->fnCollection.count;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FN_RETURN;
|
||||
tmp->returns.returns = returns;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_IF;
|
||||
tmp->pathIf.condition = condition;
|
||||
tmp->pathIf.thenPath = thenPath;
|
||||
tmp->pathIf.elsePath = elsePath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_WHILE;
|
||||
tmp->pathWhile.condition = condition;
|
||||
tmp->pathWhile.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_FOR;
|
||||
tmp->pathFor.preClause = preClause;
|
||||
tmp->pathFor.condition = condition;
|
||||
tmp->pathFor.postClause = postClause;
|
||||
tmp->pathFor.thenPath = thenPath;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_BREAK;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_CONTINUE;
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PREFIX_INCREMENT;
|
||||
tmp->prefixIncrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_PREFIX_DECREMENT;
|
||||
tmp->prefixDecrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_POSTFIX_INCREMENT;
|
||||
tmp->postfixIncrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_POSTFIX_DECREMENT;
|
||||
tmp->postfixDecrement.identifier = Toy_copyLiteral(identifier);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
|
||||
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias) {
|
||||
Toy_ASTNode* tmp = TOY_ALLOCATE(Toy_ASTNode, 1);
|
||||
|
||||
tmp->type = TOY_AST_NODE_IMPORT;
|
||||
tmp->import.identifier = Toy_copyLiteral(identifier);
|
||||
tmp->import.alias = Toy_copyLiteral(alias);
|
||||
|
||||
*nodeHandle = tmp;
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_literal.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_token_types.h"
|
||||
|
||||
//nodes are the intermediaries between parsers and compilers
|
||||
typedef union Toy_private_node Toy_ASTNode;
|
||||
|
||||
typedef enum Toy_ASTNodeType {
|
||||
TOY_AST_NODE_ERROR,
|
||||
TOY_AST_NODE_LITERAL, //a simple value
|
||||
TOY_AST_NODE_UNARY, //one child + opcode
|
||||
TOY_AST_NODE_BINARY, //two children, left and right + opcode
|
||||
TOY_AST_NODE_TERNARY, //three children, condition, then path & else path
|
||||
TOY_AST_NODE_GROUPING, //one child
|
||||
TOY_AST_NODE_BLOCK, //contains a sub-node array
|
||||
TOY_AST_NODE_COMPOUND, //contains a sub-node array
|
||||
TOY_AST_NODE_PAIR, //contains a left and right
|
||||
TOY_AST_NODE_INDEX, //index a variable
|
||||
TOY_AST_NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
|
||||
TOY_AST_NODE_FN_DECL, //containd identifier literal, arguments node, returns node, block node
|
||||
TOY_AST_NODE_FN_COLLECTION, //parts of a function
|
||||
TOY_AST_NODE_FN_CALL, //call a function
|
||||
TOY_AST_NODE_FN_RETURN, //for control flow
|
||||
TOY_AST_NODE_IF, //for control flow
|
||||
TOY_AST_NODE_WHILE, //for control flow
|
||||
TOY_AST_NODE_FOR, //for control flow
|
||||
TOY_AST_NODE_BREAK, //for control flow
|
||||
TOY_AST_NODE_CONTINUE, //for control flow
|
||||
TOY_AST_NODE_PREFIX_INCREMENT, //increment a variable
|
||||
TOY_AST_NODE_POSTFIX_INCREMENT, //increment a variable
|
||||
TOY_AST_NODE_PREFIX_DECREMENT, //decrement a variable
|
||||
TOY_AST_NODE_POSTFIX_DECREMENT, //decrement a variable
|
||||
TOY_AST_NODE_IMPORT, //import a library
|
||||
} Toy_ASTNodeType;
|
||||
|
||||
//literals
|
||||
void Toy_emitASTNodeLiteral(Toy_ASTNode** nodeHandle, Toy_Literal literal);
|
||||
|
||||
typedef struct Toy_NodeLiteral {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal literal;
|
||||
} Toy_NodeLiteral;
|
||||
|
||||
//unary operator
|
||||
void Toy_emitASTNodeUnary(Toy_ASTNode** nodeHandle, Toy_Opcode opcode, Toy_ASTNode* child);
|
||||
|
||||
typedef struct Toy_NodeUnary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Opcode opcode;
|
||||
Toy_ASTNode* child;
|
||||
} Toy_NodeUnary;
|
||||
|
||||
//binary operator
|
||||
void Toy_emitASTNodeBinary(Toy_ASTNode** nodeHandle, Toy_ASTNode* rhs, Toy_Opcode opcode); //handled node becomes lhs
|
||||
|
||||
typedef struct Toy_NodeBinary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Opcode opcode;
|
||||
Toy_ASTNode* left;
|
||||
Toy_ASTNode* right;
|
||||
} Toy_NodeBinary;
|
||||
|
||||
//ternary operator
|
||||
void Toy_emitASTNodeTernary(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
|
||||
|
||||
typedef struct Toy_NodeTernary {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
Toy_ASTNode* elsePath;
|
||||
} Toy_NodeTernary;
|
||||
|
||||
//grouping of other AST nodes
|
||||
void Toy_emitASTNodeGrouping(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeGrouping {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* child;
|
||||
} Toy_NodeGrouping;
|
||||
|
||||
//block of statement nodes
|
||||
void Toy_emitASTNodeBlock(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeBlock {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeBlock;
|
||||
|
||||
//compound literals (array, dictionary)
|
||||
void Toy_emitASTNodeCompound(Toy_ASTNode** nodeHandle, Toy_LiteralType literalType);
|
||||
|
||||
typedef struct Toy_NodeCompound {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_LiteralType literalType;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeCompound;
|
||||
|
||||
void Toy_setASTNodePair(Toy_ASTNode* node, Toy_ASTNode* left, Toy_ASTNode* right); //NOTE: this is a set function, not an emit function
|
||||
|
||||
typedef struct Toy_NodePair {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* left;
|
||||
Toy_ASTNode* right;
|
||||
} Toy_NodePair;
|
||||
|
||||
void Toy_emitASTNodeIndex(Toy_ASTNode** nodeHandle, Toy_ASTNode* first, Toy_ASTNode* second, Toy_ASTNode* third);
|
||||
|
||||
typedef struct Toy_NodeIndex {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* first;
|
||||
Toy_ASTNode* second;
|
||||
Toy_ASTNode* third;
|
||||
} Toy_NodeIndex;
|
||||
|
||||
//variable declaration
|
||||
void Toy_emitASTNodeVarDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal type, Toy_ASTNode* expression);
|
||||
|
||||
typedef struct Toy_NodeVarDecl {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_Literal typeLiteral;
|
||||
Toy_ASTNode* expression;
|
||||
} Toy_NodeVarDecl;
|
||||
|
||||
//NOTE: fnCollection is used by fnDecl, fnCall and fnReturn
|
||||
void Toy_emitASTNodeFnCollection(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeFnCollection {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* nodes;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_NodeFnCollection;
|
||||
|
||||
//function declaration
|
||||
void Toy_emitASTNodeFnDecl(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_ASTNode* arguments, Toy_ASTNode* returns, Toy_ASTNode* block);
|
||||
|
||||
typedef struct Toy_NodeFnDecl {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_ASTNode* arguments;
|
||||
Toy_ASTNode* returns;
|
||||
Toy_ASTNode* block;
|
||||
} Toy_NodeFnDecl;
|
||||
|
||||
//function call
|
||||
void Toy_emitASTNodeFnCall(Toy_ASTNode** nodeHandle, Toy_ASTNode* arguments);
|
||||
|
||||
typedef struct Toy_NodeFnCall {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* arguments;
|
||||
int argumentCount; //NOTE: leave this, so it can be hacked by dottify()
|
||||
} Toy_NodeFnCall;
|
||||
|
||||
//function return
|
||||
void Toy_emitASTNodeFnReturn(Toy_ASTNode** nodeHandle, Toy_ASTNode* returns);
|
||||
|
||||
typedef struct Toy_NodeFnReturn {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* returns;
|
||||
} Toy_NodeFnReturn;
|
||||
|
||||
//control flow path - if-else, while, for, break, continue, return
|
||||
void Toy_emitASTNodeIf(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath, Toy_ASTNode* elsePath);
|
||||
void Toy_emitASTNodeWhile(Toy_ASTNode** nodeHandle, Toy_ASTNode* condition, Toy_ASTNode* thenPath);
|
||||
void Toy_emitASTNodeFor(Toy_ASTNode** nodeHandle, Toy_ASTNode* preClause, Toy_ASTNode* condition, Toy_ASTNode* postClause, Toy_ASTNode* thenPath);
|
||||
void Toy_emitASTNodeBreak(Toy_ASTNode** nodeHandle);
|
||||
void Toy_emitASTNodeContinue(Toy_ASTNode** nodeHandle);
|
||||
|
||||
typedef struct Toy_NodeIf {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
Toy_ASTNode* elsePath;
|
||||
} Toy_NodeIf;
|
||||
|
||||
typedef struct Toy_NodeWhile {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* thenPath;
|
||||
} Toy_NodeWhile;
|
||||
|
||||
typedef struct Toy_NodeFor {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_ASTNode* preClause;
|
||||
Toy_ASTNode* condition;
|
||||
Toy_ASTNode* postClause;
|
||||
Toy_ASTNode* thenPath;
|
||||
} Toy_NodeFor;
|
||||
|
||||
typedef struct Toy_NodeBreak {
|
||||
Toy_ASTNodeType type;
|
||||
} Toy_NodeBreak;
|
||||
|
||||
typedef struct Toy_NodeContinue {
|
||||
Toy_ASTNodeType type;
|
||||
} Toy_NodeContinue;
|
||||
|
||||
//pre-post increment/decrement
|
||||
void Toy_emitASTNodePrefixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePrefixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePostfixIncrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
void Toy_emitASTNodePostfixDecrement(Toy_ASTNode** nodeHandle, Toy_Literal identifier);
|
||||
|
||||
typedef struct Toy_NodePrefixIncrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePrefixIncrement;
|
||||
|
||||
typedef struct Toy_NodePrefixDecrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePrefixDecrement;
|
||||
|
||||
typedef struct Toy_NodePostfixIncrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePostfixIncrement;
|
||||
|
||||
typedef struct Toy_NodePostfixDecrement {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
} Toy_NodePostfixDecrement;
|
||||
|
||||
//import a library
|
||||
void Toy_emitASTNodeImport(Toy_ASTNode** nodeHandle, Toy_Literal identifier, Toy_Literal alias);
|
||||
|
||||
typedef struct Toy_NodeImport {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_Literal identifier;
|
||||
Toy_Literal alias;
|
||||
} Toy_NodeImport;
|
||||
|
||||
union Toy_private_node {
|
||||
Toy_ASTNodeType type;
|
||||
Toy_NodeLiteral atomic;
|
||||
Toy_NodeUnary unary;
|
||||
Toy_NodeBinary binary;
|
||||
Toy_NodeTernary ternary;
|
||||
Toy_NodeGrouping grouping;
|
||||
Toy_NodeBlock block;
|
||||
Toy_NodeCompound compound;
|
||||
Toy_NodePair pair;
|
||||
Toy_NodeIndex index;
|
||||
Toy_NodeVarDecl varDecl;
|
||||
Toy_NodeFnCollection fnCollection;
|
||||
Toy_NodeFnDecl fnDecl;
|
||||
Toy_NodeFnCall fnCall;
|
||||
Toy_NodeFnReturn returns;
|
||||
Toy_NodeIf pathIf;
|
||||
Toy_NodeWhile pathWhile;
|
||||
Toy_NodeFor pathFor;
|
||||
Toy_NodeBreak pathBreak;
|
||||
Toy_NodeContinue pathContinue;
|
||||
Toy_NodePrefixIncrement prefixIncrement;
|
||||
Toy_NodePrefixDecrement prefixDecrement;
|
||||
Toy_NodePostfixIncrement postfixIncrement;
|
||||
Toy_NodePostfixDecrement postfixDecrement;
|
||||
Toy_NodeImport import;
|
||||
};
|
||||
|
||||
TOY_API void Toy_freeASTNode(Toy_ASTNode* node);
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_interpreter.h"
|
||||
|
||||
//the _index function is a historical oddity - it's used whenever a compound is indexed
|
||||
int _index(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
|
||||
//globally available native functions
|
||||
int _set(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _get(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _push(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _pop(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _length(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
int _clear(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
@@ -1,125 +0,0 @@
|
||||
#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 Toy_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 Toy_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 Toy_helpCommand(int argc, const char* argv[]) {
|
||||
Toy_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 Toy_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
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 8
|
||||
#define TOY_VERSION_PATCH 0
|
||||
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
|
||||
|
||||
//platform-specific specifications
|
||||
#if defined(__linux__)
|
||||
#define TOY_API extern
|
||||
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
#define TOY_API
|
||||
|
||||
#else
|
||||
#define TOY_API
|
||||
|
||||
#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 Toy_initCommand(int argc, const char* argv[]);
|
||||
|
||||
void Toy_usageCommand(int argc, const char* argv[]);
|
||||
void Toy_helpCommand(int argc, const char* argv[]);
|
||||
void Toy_copyrightCommand(int argc, const char* argv[]);
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_ast_node.h"
|
||||
#include "toy_literal_array.h"
|
||||
|
||||
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
||||
typedef struct Toy_Compiler {
|
||||
Toy_LiteralArray literalCache;
|
||||
unsigned char* bytecode;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_Compiler;
|
||||
|
||||
TOY_API void Toy_initCompiler(Toy_Compiler* compiler);
|
||||
TOY_API void Toy_writeCompiler(Toy_Compiler* compiler, Toy_ASTNode* node);
|
||||
TOY_API void Toy_freeCompiler(Toy_Compiler* compiler);
|
||||
|
||||
//embed the header, data section, code section, function section, etc.
|
||||
TOY_API unsigned char* Toy_collateCompiler(Toy_Compiler* compiler, int* size);
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//NOTE: you need both font AND background for these to work
|
||||
|
||||
//fonts color
|
||||
#define TOY_CC_FONT_BLACK "\033[30;"
|
||||
#define TOY_CC_FONT_RED "\033[31;"
|
||||
#define TOY_CC_FONT_GREEN "\033[32;"
|
||||
#define TOY_CC_FONT_YELLOW "\033[33;"
|
||||
#define TOY_CC_FONT_BLUE "\033[34;"
|
||||
#define TOY_CC_FONT_PURPLE "\033[35;"
|
||||
#define TOY_CC_FONT_DGREEN "\033[6;"
|
||||
#define TOY_CC_FONT_WHITE "\033[7;"
|
||||
#define TOY_CC_FONT_CYAN "\x1b[36m"
|
||||
|
||||
//background color
|
||||
#define TOY_CC_BACK_BLACK "40m"
|
||||
#define TOY_CC_BACK_RED "41m"
|
||||
#define TOY_CC_BACK_GREEN "42m"
|
||||
#define TOY_CC_BACK_YELLOW "43m"
|
||||
#define TOY_CC_BACK_BLUE "44m"
|
||||
#define TOY_CC_BACK_PURPLE "45m"
|
||||
#define TOY_CC_BACK_DGREEN "46m"
|
||||
#define TOY_CC_BACK_WHITE "47m"
|
||||
|
||||
//useful
|
||||
#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK
|
||||
#define TOY_CC_RESET "\033[0m"
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_literal.h"
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
typedef void (*Toy_PrintFn)(const char*);
|
||||
|
||||
//the interpreter acts depending on the bytecode instructions
|
||||
typedef struct Toy_Interpreter {
|
||||
//input
|
||||
unsigned char* bytecode;
|
||||
int length;
|
||||
int count;
|
||||
int codeStart; //BUGFIX: for jumps, must be initialized to -1
|
||||
Toy_LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||
|
||||
//operation
|
||||
Toy_Scope* scope;
|
||||
Toy_LiteralArray stack;
|
||||
|
||||
//Library APIs
|
||||
Toy_LiteralDictionary* hooks;
|
||||
|
||||
//debug outputs
|
||||
Toy_PrintFn printOutput;
|
||||
Toy_PrintFn assertOutput;
|
||||
Toy_PrintFn errorOutput;
|
||||
|
||||
int depth; //don't overflow
|
||||
bool panic;
|
||||
} Toy_Interpreter;
|
||||
|
||||
//native API
|
||||
typedef int (*Toy_NativeFn)(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments);
|
||||
TOY_API bool Toy_injectNativeFn(Toy_Interpreter* interpreter, char* name, Toy_NativeFn func);
|
||||
|
||||
typedef int (*Toy_HookFn)(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias);
|
||||
TOY_API bool Toy_injectNativeHook(Toy_Interpreter* interpreter, char* name, Toy_HookFn hook);
|
||||
|
||||
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
TOY_API bool Toy_callFn(Toy_Interpreter* interpreter, char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||
|
||||
//utilities for the host program
|
||||
TOY_API bool Toy_parseIdentifierToValue(Toy_Interpreter* interpreter, Toy_Literal* literalPtr);
|
||||
TOY_API void Toy_setInterpreterPrint(Toy_Interpreter* interpreter, Toy_PrintFn printOutput);
|
||||
TOY_API void Toy_setInterpreterAssert(Toy_Interpreter* interpreter, Toy_PrintFn assertOutput);
|
||||
TOY_API void Toy_setInterpreterError(Toy_Interpreter* interpreter, Toy_PrintFn errorOutput);
|
||||
|
||||
//main access
|
||||
TOY_API void Toy_initInterpreter(Toy_Interpreter* interpreter); //start of program
|
||||
TOY_API void Toy_runInterpreter(Toy_Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
|
||||
TOY_API void Toy_resetInterpreter(Toy_Interpreter* interpreter); //use this to reset the interpreter's environment between runs
|
||||
TOY_API void Toy_freeInterpreter(Toy_Interpreter* interpreter); //end of program
|
||||
@@ -1,77 +0,0 @@
|
||||
#include "toy_keyword_types.h"
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
Toy_KeywordType Toy_keywordTypes[] = {
|
||||
//type keywords
|
||||
{TOY_TOKEN_NULL, "null"},
|
||||
{TOY_TOKEN_BOOLEAN, "bool"},
|
||||
{TOY_TOKEN_INTEGER, "int"},
|
||||
{TOY_TOKEN_FLOAT, "float"},
|
||||
{TOY_TOKEN_STRING, "string"},
|
||||
{TOY_TOKEN_FUNCTION, "fn"},
|
||||
{TOY_TOKEN_OPAQUE, "opaque"},
|
||||
{TOY_TOKEN_ANY, "any"},
|
||||
|
||||
//other keywords
|
||||
{TOY_TOKEN_AS, "as"},
|
||||
{TOY_TOKEN_ASSERT, "assert"},
|
||||
{TOY_TOKEN_BREAK, "break"},
|
||||
{TOY_TOKEN_CLASS, "class"},
|
||||
{TOY_TOKEN_CONST, "const"},
|
||||
{TOY_TOKEN_CONTINUE, "continue"},
|
||||
{TOY_TOKEN_DO, "do"},
|
||||
{TOY_TOKEN_ELSE, "else"},
|
||||
{TOY_TOKEN_EXPORT, "export"},
|
||||
{TOY_TOKEN_FOR, "for"},
|
||||
{TOY_TOKEN_FOREACH, "foreach"},
|
||||
{TOY_TOKEN_IF, "if"},
|
||||
{TOY_TOKEN_IMPORT, "import"},
|
||||
{TOY_TOKEN_IN, "in"},
|
||||
{TOY_TOKEN_OF, "of"},
|
||||
{TOY_TOKEN_PRINT, "print"},
|
||||
{TOY_TOKEN_RETURN, "return"},
|
||||
{TOY_TOKEN_TYPE, "type"},
|
||||
{TOY_TOKEN_ASTYPE, "astype"},
|
||||
{TOY_TOKEN_TYPEOF, "typeof"},
|
||||
{TOY_TOKEN_VAR, "var"},
|
||||
{TOY_TOKEN_WHILE, "while"},
|
||||
|
||||
//literal values
|
||||
{TOY_TOKEN_LITERAL_TRUE, "true"},
|
||||
{TOY_TOKEN_LITERAL_FALSE, "false"},
|
||||
|
||||
//meta tokens
|
||||
{TOY_TOKEN_PASS, NULL},
|
||||
{TOY_TOKEN_ERROR, NULL},
|
||||
|
||||
{TOY_TOKEN_EOF, NULL},
|
||||
};
|
||||
|
||||
char* Toy_findKeywordByType(Toy_TokenType type) {
|
||||
if (type == TOY_TOKEN_EOF) {
|
||||
return "EOF";
|
||||
}
|
||||
|
||||
for(int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (Toy_keywordTypes[i].type == type) {
|
||||
return Toy_keywordTypes[i].keyword;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_TokenType Toy_findTypeByKeyword(const char* keyword) {
|
||||
const int length = strlen(keyword);
|
||||
|
||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (!strncmp(keyword, Toy_keywordTypes[i].keyword, length)) {
|
||||
return Toy_keywordTypes[i].type;
|
||||
}
|
||||
}
|
||||
|
||||
return TOY_TOKEN_EOF;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_token_types.h"
|
||||
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
char* keyword;
|
||||
} Toy_KeywordType;
|
||||
|
||||
extern Toy_KeywordType Toy_keywordTypes[];
|
||||
|
||||
char* Toy_findKeywordByType(Toy_TokenType type);
|
||||
|
||||
Toy_TokenType Toy_findTypeByKeyword(const char* keyword);
|
||||
@@ -1,349 +0,0 @@
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_console_colors.h"
|
||||
#include "toy_keyword_types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//static generic utility functions
|
||||
static void cleanLexer(Toy_Lexer* lexer) {
|
||||
lexer->source = NULL;
|
||||
lexer->start = 0;
|
||||
lexer->current = 0;
|
||||
lexer->line = 1;
|
||||
}
|
||||
|
||||
static bool isAtEnd(Toy_Lexer* lexer) {
|
||||
return lexer->source[lexer->current] == '\0';
|
||||
}
|
||||
|
||||
static char peek(Toy_Lexer* lexer) {
|
||||
return lexer->source[lexer->current];
|
||||
}
|
||||
|
||||
static char peekNext(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) return '\0';
|
||||
return lexer->source[lexer->current + 1];
|
||||
}
|
||||
|
||||
static char advance(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
//new line
|
||||
if (lexer->source[lexer->current] == '\n') {
|
||||
lexer->line++;
|
||||
}
|
||||
|
||||
lexer->current++;
|
||||
return lexer->source[lexer->current - 1];
|
||||
}
|
||||
|
||||
static void eatWhitespace(Toy_Lexer* lexer) {
|
||||
const char c = peek(lexer);
|
||||
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
advance(lexer);
|
||||
break;
|
||||
|
||||
//comments
|
||||
case '/':
|
||||
//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(Toy_Lexer* lexer) {
|
||||
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(Toy_Lexer* lexer) {
|
||||
return
|
||||
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
||||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
||||
peek(lexer) == '_'
|
||||
;
|
||||
}
|
||||
|
||||
static bool match(Toy_Lexer* lexer, char c) {
|
||||
if (peek(lexer) == c) {
|
||||
advance(lexer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//token generators
|
||||
static Toy_Token makeErrorToken(Toy_Lexer* lexer, char* msg) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_ERROR;
|
||||
token.lexeme = msg;
|
||||
token.length = strlen(msg);
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("err:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeToken(Toy_Lexer* lexer, Toy_TokenType type) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = type;
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.lexeme = &lexer->source[lexer->current - token.length];
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
|
||||
if (command.verbose) {
|
||||
printf("tok:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
||||
Toy_TokenType type = TOY_TOKEN_LITERAL_INTEGER; //what am I making?
|
||||
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
|
||||
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
|
||||
type = TOY_TOKEN_LITERAL_FLOAT;
|
||||
advance(lexer);
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
|
||||
token.type = type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
if (type == TOY_TOKEN_LITERAL_INTEGER) {
|
||||
printf("int:");
|
||||
} else {
|
||||
printf("flt:");
|
||||
}
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
advance(lexer); //eat terminator
|
||||
|
||||
if (isAtEnd(lexer)) {
|
||||
return makeErrorToken(lexer, "Unterminated string");
|
||||
}
|
||||
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_LITERAL_STRING;
|
||||
token.lexeme = &lexer->source[lexer->start + 1];
|
||||
token.length = lexer->current - lexer->start - 2;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("str:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
advance(lexer); //first letter can only be alpha
|
||||
|
||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
//scan for a keyword
|
||||
for (int i = 0; Toy_keywordTypes[i].keyword; i++) {
|
||||
if (strlen(Toy_keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(Toy_keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||
Toy_Token token;
|
||||
|
||||
token.type = Toy_keywordTypes[i].type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("kwd:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
//return an identifier
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_IDENTIFIER;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
#ifndef TOY_EXPORT
|
||||
if (command.verbose) {
|
||||
printf("idf:");
|
||||
Toy_printToken(&token);
|
||||
}
|
||||
#endif
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLexer(Toy_Lexer* lexer, char* source) {
|
||||
cleanLexer(lexer);
|
||||
|
||||
lexer->source = source;
|
||||
}
|
||||
|
||||
Toy_Token Toy_scanLexer(Toy_Lexer* lexer) {
|
||||
eatWhitespace(lexer);
|
||||
|
||||
lexer->start = lexer->current;
|
||||
|
||||
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
|
||||
|
||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||
|
||||
char c = advance(lexer);
|
||||
|
||||
switch(c) {
|
||||
case '(': return makeToken(lexer, TOY_TOKEN_PAREN_LEFT);
|
||||
case ')': return makeToken(lexer, TOY_TOKEN_PAREN_RIGHT);
|
||||
case '{': return makeToken(lexer, TOY_TOKEN_BRACE_LEFT);
|
||||
case '}': return makeToken(lexer, TOY_TOKEN_BRACE_RIGHT);
|
||||
case '[': return makeToken(lexer, TOY_TOKEN_BRACKET_LEFT);
|
||||
case ']': return makeToken(lexer, TOY_TOKEN_BRACKET_RIGHT);
|
||||
|
||||
case '+': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOY_TOKEN_PLUS_PLUS: TOY_TOKEN_PLUS);
|
||||
case '-': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOY_TOKEN_MINUS_MINUS: TOY_TOKEN_MINUS);
|
||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MULTIPLY_ASSIGN : TOY_TOKEN_MULTIPLY);
|
||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_DIVIDE_ASSIGN : TOY_TOKEN_DIVIDE);
|
||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_MODULO_ASSIGN : TOY_TOKEN_MODULO);
|
||||
|
||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_NOT_EQUAL : TOY_TOKEN_NOT);
|
||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_EQUAL : TOY_TOKEN_ASSIGN);
|
||||
|
||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_LESS_EQUAL : TOY_TOKEN_LESS);
|
||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOY_TOKEN_GREATER_EQUAL : TOY_TOKEN_GREATER);
|
||||
|
||||
case '&': //TOKEN_AND not used
|
||||
if (advance(lexer) != '&') {
|
||||
return makeErrorToken(lexer, "Unexpected '&'");
|
||||
} else {
|
||||
return makeToken(lexer, TOY_TOKEN_AND);
|
||||
}
|
||||
|
||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOY_TOKEN_OR : TOY_TOKEN_PIPE);
|
||||
|
||||
case '?': return makeToken(lexer, TOY_TOKEN_QUESTION);
|
||||
case ':': return makeToken(lexer, TOY_TOKEN_COLON);
|
||||
case ';': return makeToken(lexer, TOY_TOKEN_SEMICOLON);
|
||||
case ',': return makeToken(lexer, TOY_TOKEN_COMMA);
|
||||
case '.':
|
||||
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
return makeToken(lexer, TOY_TOKEN_REST);
|
||||
}
|
||||
return makeToken(lexer, TOY_TOKEN_DOT);
|
||||
|
||||
case '"':
|
||||
return makeString(lexer, c);
|
||||
//TODO: possibly support interpolated strings
|
||||
|
||||
default: {
|
||||
char buffer[128];
|
||||
snprintf(buffer, 128, "Unexpected token: %c", c);
|
||||
return makeErrorToken(lexer, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void trim(char** s, int* l) { //all this to remove a newline?
|
||||
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
|
||||
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
|
||||
}
|
||||
|
||||
//for debugging
|
||||
void Toy_printToken(Toy_Token* token) {
|
||||
if (token->type == TOY_TOKEN_ERROR) {
|
||||
printf(TOY_CC_ERROR "Error\t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\t%d\t%d\t", token->type, token->line);
|
||||
|
||||
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
|
||||
printf("%.*s\t", token->length, token->lexeme);
|
||||
} else {
|
||||
char* keyword = Toy_findKeywordByType(token->type);
|
||||
|
||||
if (keyword != NULL) {
|
||||
printf("%s", keyword);
|
||||
} else {
|
||||
char* str = token->lexeme;
|
||||
int length = token->length;
|
||||
trim(&str, &length);
|
||||
printf("%.*s", length, str);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_token_types.h"
|
||||
|
||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||
typedef struct {
|
||||
char* source;
|
||||
int start; //start of the token
|
||||
int current; //current position of the lexer
|
||||
int line; //track this for error handling
|
||||
} Toy_Lexer;
|
||||
|
||||
//tokens are intermediaries between lexers and parsers
|
||||
typedef struct {
|
||||
Toy_TokenType type;
|
||||
char* lexeme;
|
||||
int length;
|
||||
int line;
|
||||
} Toy_Token;
|
||||
|
||||
TOY_API void Toy_initLexer(Toy_Lexer* lexer, char* source);
|
||||
Toy_Token Toy_scanLexer(Toy_Lexer* lexer);
|
||||
|
||||
//for debugging
|
||||
void Toy_printToken(Toy_Token* token);
|
||||
@@ -1,705 +0,0 @@
|
||||
#include "toy_literal.h"
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//hash util functions
|
||||
static unsigned int hashString(const char* string, int length) {
|
||||
unsigned int hash = 2166136261u;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
hash *= string[i];
|
||||
hash ^= 16777619;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned int hashUInt(unsigned int x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_freeLiteral(Toy_Literal literal) {
|
||||
//refstrings
|
||||
if (TOY_IS_STRING(literal)) {
|
||||
Toy_deleteRefString(TOY_AS_STRING(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_IDENTIFIER(literal)) {
|
||||
Toy_deleteRefString(TOY_AS_IDENTIFIER(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//compounds
|
||||
if (TOY_IS_ARRAY(literal) || literal.type == TOY_LITERAL_DICTIONARY_INTERMEDIATE || literal.type == TOY_LITERAL_TYPE_INTERMEDIATE) {
|
||||
Toy_freeLiteralArray(TOY_AS_ARRAY(literal));
|
||||
TOY_FREE(Toy_LiteralArray, TOY_AS_ARRAY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_DICTIONARY(literal)) {
|
||||
Toy_freeLiteralDictionary(TOY_AS_DICTIONARY(literal));
|
||||
TOY_FREE(Toy_LiteralDictionary, TOY_AS_DICTIONARY(literal));
|
||||
return;
|
||||
}
|
||||
|
||||
//complex literals
|
||||
if (TOY_IS_FUNCTION(literal)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(literal).scope);
|
||||
TOY_AS_FUNCTION(literal).scope = NULL;
|
||||
TOY_FREE_ARRAY(unsigned char, TOY_AS_FUNCTION(literal).bytecode, TOY_AS_FUNCTION(literal).length);
|
||||
}
|
||||
|
||||
if (TOY_IS_TYPE(literal)) {
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||
Toy_freeLiteral(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i]);
|
||||
}
|
||||
TOY_FREE_ARRAY(Toy_Literal, TOY_AS_TYPE(literal).subtypes, TOY_AS_TYPE(literal).capacity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_private_isTruthy(Toy_Literal x) {
|
||||
if (TOY_IS_NULL(x)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Null is neither true nor false\n" TOY_CC_RESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_BOOLEAN(x)) {
|
||||
return TOY_AS_BOOLEAN(x);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr) {
|
||||
return ((Toy_Literal){TOY_LITERAL_STRING, { .string.ptr = ptr }});
|
||||
}
|
||||
|
||||
Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr) {
|
||||
return ((Toy_Literal){TOY_LITERAL_IDENTIFIER,{ .identifier.ptr = ptr, .identifier.hash = hashString(Toy_toCString(ptr), Toy_lengthRefString(ptr)) }});
|
||||
}
|
||||
|
||||
Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype) {
|
||||
//grow the subtype array
|
||||
if (TOY_AS_TYPE(*lit).count + 1 > TOY_AS_TYPE(*lit).capacity) {
|
||||
int oldCapacity = TOY_AS_TYPE(*lit).capacity;
|
||||
|
||||
TOY_AS_TYPE(*lit).capacity = TOY_GROW_CAPACITY(oldCapacity);
|
||||
TOY_AS_TYPE(*lit).subtypes = TOY_GROW_ARRAY(Toy_Literal, TOY_AS_TYPE(*lit).subtypes, oldCapacity, TOY_AS_TYPE(*lit).capacity);
|
||||
}
|
||||
|
||||
//actually push
|
||||
((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count++ ] = subtype;
|
||||
return &((Toy_Literal*)(TOY_AS_TYPE(*lit).subtypes))[ TOY_AS_TYPE(*lit).count - 1 ];
|
||||
}
|
||||
|
||||
Toy_Literal Toy_copyLiteral(Toy_Literal original) {
|
||||
switch(original.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
case TOY_LITERAL_INTEGER:
|
||||
case TOY_LITERAL_FLOAT:
|
||||
//no copying needed
|
||||
return original;
|
||||
|
||||
case TOY_LITERAL_STRING: {
|
||||
return TOY_TO_STRING_LITERAL(Toy_copyRefString(TOY_AS_STRING(original)));
|
||||
}
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_pushLiteralArray(array, TOY_AS_ARRAY(original)->literals[i]);
|
||||
}
|
||||
|
||||
return TOY_TO_ARRAY_LITERAL(array);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
Toy_LiteralDictionary* dictionary = TOY_ALLOCATE(Toy_LiteralDictionary, 1);
|
||||
Toy_initLiteralDictionary(dictionary);
|
||||
|
||||
//copy each entry
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(original)->capacity; i++) {
|
||||
if ( !TOY_IS_NULL(TOY_AS_DICTIONARY(original)->entries[i].key) ) {
|
||||
Toy_setLiteralDictionary(dictionary, TOY_AS_DICTIONARY(original)->entries[i].key, TOY_AS_DICTIONARY(original)->entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return TOY_TO_DICTIONARY_LITERAL(dictionary);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION: {
|
||||
unsigned char* buffer = TOY_ALLOCATE(unsigned char, TOY_AS_FUNCTION(original).length);
|
||||
memcpy(buffer, TOY_AS_FUNCTION(original).bytecode, TOY_AS_FUNCTION(original).length);
|
||||
|
||||
Toy_Literal literal = TOY_TO_FUNCTION_LITERAL(buffer, TOY_AS_FUNCTION(original).length);
|
||||
TOY_AS_FUNCTION(literal).scope = Toy_copyScope(TOY_AS_FUNCTION(original).scope);
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
return TOY_TO_IDENTIFIER_LITERAL(Toy_copyRefString(TOY_AS_IDENTIFIER(original)));
|
||||
}
|
||||
|
||||
case TOY_LITERAL_TYPE: {
|
||||
Toy_Literal lit = TOY_TO_TYPE_LITERAL(TOY_AS_TYPE(original).typeOf, TOY_AS_TYPE(original).constant);
|
||||
|
||||
for (int i = 0; i < TOY_AS_TYPE(original).count; i++) {
|
||||
TOY_TYPE_PUSH_SUBTYPE(&lit, Toy_copyLiteral( ((Toy_Literal*)(TOY_AS_TYPE(original).subtypes))[i] ));
|
||||
}
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_OPAQUE: {
|
||||
return original; //literally a shallow copy
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_DICTIONARY_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: {
|
||||
Toy_LiteralArray* array = TOY_ALLOCATE(Toy_LiteralArray, 1);
|
||||
Toy_initLiteralArray(array);
|
||||
|
||||
//copy each element
|
||||
for (int i = 0; i < TOY_AS_ARRAY(original)->count; i++) {
|
||||
Toy_Literal literal = Toy_copyLiteral(TOY_AS_ARRAY(original)->literals[i]);
|
||||
Toy_pushLiteralArray(array, literal);
|
||||
Toy_freeLiteral(literal);
|
||||
}
|
||||
|
||||
Toy_Literal ret = TOY_TO_ARRAY_LITERAL(array);
|
||||
ret.type = TOY_LITERAL_TYPE_INTERMEDIATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
//no copying possible
|
||||
return original;
|
||||
|
||||
default:
|
||||
fprintf(stderr, TOY_CC_ERROR "TOY_CC_ERROR: Can't copy that literal type: %d\n" TOY_CC_RESET, original.type);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs) {
|
||||
//utility for other things
|
||||
if (lhs.type != rhs.type) {
|
||||
// ints and floats are compatible
|
||||
if ((TOY_IS_INTEGER(lhs) || TOY_IS_FLOAT(lhs)) && (TOY_IS_INTEGER(rhs) || TOY_IS_FLOAT(rhs))) {
|
||||
if (TOY_IS_INTEGER(lhs)) {
|
||||
return TOY_AS_INTEGER(lhs) + TOY_AS_FLOAT(rhs);
|
||||
}
|
||||
else {
|
||||
return TOY_AS_FLOAT(lhs) + TOY_AS_INTEGER(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(lhs.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
return true; //can only be true because of the check above
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
return TOY_AS_BOOLEAN(lhs) == TOY_AS_BOOLEAN(rhs);
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
return TOY_AS_INTEGER(lhs) == TOY_AS_INTEGER(rhs);
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
return TOY_AS_FLOAT(lhs) == TOY_AS_FLOAT(rhs);
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
return Toy_equalsRefString(TOY_AS_STRING(lhs), TOY_AS_STRING(rhs));
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
case TOY_LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
|
||||
//mismatched sizes
|
||||
if (TOY_AS_ARRAY(lhs)->count != TOY_AS_ARRAY(rhs)->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//mismatched elements (in order)
|
||||
for (int i = 0; i < TOY_AS_ARRAY(lhs)->count; i++) {
|
||||
if (!Toy_literalsAreEqual( TOY_AS_ARRAY(lhs)->literals[i], TOY_AS_ARRAY(rhs)->literals[i] )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY:
|
||||
//relatively slow, especially when nested
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(lhs)->capacity; i++) {
|
||||
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
|
||||
//check it exists in rhs
|
||||
if (!Toy_existsLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//compare the values
|
||||
Toy_Literal val = Toy_getLiteralDictionary(TOY_AS_DICTIONARY(rhs), TOY_AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
|
||||
if (!Toy_literalsAreEqual(TOY_AS_DICTIONARY(lhs)->entries[i].value, val)) {
|
||||
Toy_freeLiteral(val);
|
||||
return false;
|
||||
}
|
||||
Toy_freeLiteral(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
return false; //functions are never equal
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
//check shortcuts
|
||||
if (TOY_HASH_I(lhs) != TOY_HASH_I(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Toy_equalsRefString(TOY_AS_IDENTIFIER(lhs), TOY_AS_IDENTIFIER(rhs));
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
//check types
|
||||
if (TOY_AS_TYPE(lhs).typeOf != TOY_AS_TYPE(rhs).typeOf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//const don't match
|
||||
if (TOY_AS_TYPE(lhs).constant != TOY_AS_TYPE(rhs).constant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check subtypes
|
||||
if (TOY_AS_TYPE(lhs).count != TOY_AS_TYPE(rhs).count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//check array|dictionary signatures are the same (in order)
|
||||
if (TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_ARRAY || TOY_AS_TYPE(lhs).typeOf == TOY_LITERAL_DICTIONARY) {
|
||||
for (int i = 0; i < TOY_AS_TYPE(lhs).count; i++) {
|
||||
if (!Toy_literalsAreEqual(((Toy_Literal*)(TOY_AS_TYPE(lhs).subtypes))[i], ((Toy_Literal*)(TOY_AS_TYPE(rhs).subtypes))[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
return false; //IDK what this is!
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
return true;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Can't compare intermediate functions\n" TOY_CC_RESET);
|
||||
return false;
|
||||
|
||||
case TOY_LITERAL_INDEX_BLANK:
|
||||
return false;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in equality: %d\n" TOY_CC_RESET, lhs.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Toy_hashLiteral(Toy_Literal lit) {
|
||||
switch(lit.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
return 0;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
return TOY_AS_BOOLEAN(lit) ? 1 : 0;
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
return hashUInt((unsigned int)TOY_AS_INTEGER(lit));
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
return hashUInt(*(unsigned int*)(&TOY_AS_FLOAT(lit)));
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
return hashString(Toy_toCString(TOY_AS_STRING(lit)), Toy_lengthRefString(TOY_AS_STRING(lit)));
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < TOY_AS_ARRAY(lit)->count; i++) {
|
||||
res += Toy_hashLiteral(TOY_AS_ARRAY(lit)->literals[i]);
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
unsigned int res = 0;
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(lit)->capacity; i++) {
|
||||
if (!TOY_IS_NULL(TOY_AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
|
||||
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].key);
|
||||
res += Toy_hashLiteral(TOY_AS_DICTIONARY(lit)->entries[i].value);
|
||||
}
|
||||
}
|
||||
return hashUInt(res);
|
||||
}
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
return 0; //can't hash these
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
return TOY_HASH_I(lit); //pre-computed
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
return TOY_AS_TYPE(lit).typeOf; //nothing else I can do
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
case TOY_LITERAL_ANY:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
//should never bee seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in hash: %d\n" TOY_CC_RESET, lit.type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//utils
|
||||
static void stdoutWrapper(const char* output) {
|
||||
printf("%s", output);
|
||||
}
|
||||
|
||||
//buffer the prints
|
||||
static char* globalPrintBuffer = NULL;
|
||||
static size_t globalPrintCapacity = 0;
|
||||
static size_t globalPrintCount = 0;
|
||||
|
||||
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
|
||||
static char quotes = 0; //set to 0 to not show string quotes
|
||||
|
||||
static void printToBuffer(const char* str) {
|
||||
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
|
||||
int oldCapacity = globalPrintCapacity;
|
||||
|
||||
globalPrintCapacity = TOY_GROW_CAPACITY(globalPrintCapacity);
|
||||
globalPrintBuffer = TOY_GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
|
||||
}
|
||||
|
||||
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
|
||||
globalPrintCount += strlen(str);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_printLiteral(Toy_Literal literal) {
|
||||
Toy_printLiteralCustom(literal, stdoutWrapper);
|
||||
}
|
||||
|
||||
void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*)) {
|
||||
switch(literal.type) {
|
||||
case TOY_LITERAL_NULL:
|
||||
printFn("null");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
printFn(TOY_AS_BOOLEAN(literal) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_INTEGER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%d", TOY_AS_INTEGER(literal));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FLOAT: {
|
||||
char buffer[256];
|
||||
|
||||
if (TOY_AS_FLOAT(literal) - (int)TOY_AS_FLOAT(literal)) {
|
||||
snprintf(buffer, 256, "%g", TOY_AS_FLOAT(literal));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, 256, "%.1f", TOY_AS_FLOAT(literal));
|
||||
}
|
||||
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_STRING: {
|
||||
char buffer[TOY_MAX_STRING_LENGTH];
|
||||
if (!quotes) {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%.*s", Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)));
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, TOY_MAX_STRING_LENGTH, "%c%.*s%c", quotes, Toy_lengthRefString(TOY_AS_STRING(literal)), Toy_toCString(TOY_AS_STRING(literal)), quotes);
|
||||
}
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ARRAY: {
|
||||
Toy_LiteralArray* ptr = TOY_AS_ARRAY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->count; i++) {
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->literals[i], printToBuffer);
|
||||
|
||||
if (i + 1 < ptr->count) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
}
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY: {
|
||||
Toy_LiteralDictionary* ptr = TOY_AS_DICTIONARY(literal);
|
||||
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the contents to the global buffer
|
||||
int delimCount = 0;
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < ptr->capacity; i++) {
|
||||
if (TOY_IS_NULL(ptr->entries[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delimCount++ > 0) {
|
||||
printToBuffer(",");
|
||||
}
|
||||
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->entries[i].key, printToBuffer);
|
||||
printToBuffer(":");
|
||||
quotes = '"';
|
||||
Toy_printLiteralCustom(ptr->entries[i].value, printToBuffer);
|
||||
}
|
||||
|
||||
//empty dicts MUST have a ":" printed
|
||||
if (ptr->count == 0) {
|
||||
printToBuffer(":");
|
||||
}
|
||||
|
||||
printToBuffer("]");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
printFn("(function)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER: {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "%.*s", Toy_lengthRefString(TOY_AS_IDENTIFIER(literal)), Toy_toCString(TOY_AS_IDENTIFIER(literal)));
|
||||
printFn(buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE: {
|
||||
//hold potential parent-call buffers on the C stack
|
||||
char* cacheBuffer = globalPrintBuffer;
|
||||
globalPrintBuffer = NULL;
|
||||
int cacheCapacity = globalPrintCapacity;
|
||||
globalPrintCapacity = 0;
|
||||
int cacheCount = globalPrintCount;
|
||||
globalPrintCount = 0;
|
||||
|
||||
//print the type correctly
|
||||
printToBuffer("<");
|
||||
|
||||
switch(TOY_AS_TYPE(literal).typeOf) {
|
||||
case TOY_LITERAL_NULL:
|
||||
printToBuffer("null");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_BOOLEAN:
|
||||
printToBuffer("bool");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_INTEGER:
|
||||
printToBuffer("int");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FLOAT:
|
||||
printToBuffer("float");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_STRING:
|
||||
printToBuffer("string");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ARRAY:
|
||||
//print all in the array
|
||||
printToBuffer("[");
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i++) {
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_DICTIONARY:
|
||||
printToBuffer("[");
|
||||
|
||||
for (int i = 0; i < TOY_AS_TYPE(literal).count; i += 2) {
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i], printToBuffer);
|
||||
printToBuffer(":");
|
||||
Toy_printLiteralCustom(((Toy_Literal*)(TOY_AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
|
||||
}
|
||||
printToBuffer("]");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION:
|
||||
printToBuffer("function");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_FUNCTION_NATIVE:
|
||||
printToBuffer("native");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_IDENTIFIER:
|
||||
printToBuffer("identifier");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE:
|
||||
printToBuffer("type");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
printToBuffer("opaque");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
printToBuffer("any");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print type: %d\n" TOY_CC_RESET, TOY_AS_TYPE(literal).typeOf);
|
||||
}
|
||||
|
||||
//const (printed last)
|
||||
if (TOY_AS_TYPE(literal).constant) {
|
||||
printToBuffer(" const");
|
||||
}
|
||||
|
||||
printToBuffer(">");
|
||||
|
||||
//swap the parent-call buffer back into place
|
||||
char* printBuffer = globalPrintBuffer;
|
||||
int printCapacity = globalPrintCapacity;
|
||||
int printCount = globalPrintCount;
|
||||
|
||||
globalPrintBuffer = cacheBuffer;
|
||||
globalPrintCapacity = cacheCapacity;
|
||||
globalPrintCount = cacheCount;
|
||||
|
||||
//finally, output and cleanup
|
||||
printFn(printBuffer);
|
||||
TOY_FREE_ARRAY(char, printBuffer, printCapacity);
|
||||
quotes = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_TYPE_INTERMEDIATE:
|
||||
case TOY_LITERAL_FUNCTION_INTERMEDIATE:
|
||||
printFn("Unprintable literal found");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_OPAQUE:
|
||||
printFn("(opaque)");
|
||||
break;
|
||||
|
||||
case TOY_LITERAL_ANY:
|
||||
printFn("(any)");
|
||||
break;
|
||||
|
||||
default:
|
||||
//should never be seen
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Unrecognized literal type in print: %d\n" TOY_CC_RESET, literal.type);
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_refstring.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
TOY_LITERAL_NULL,
|
||||
TOY_LITERAL_BOOLEAN,
|
||||
TOY_LITERAL_INTEGER,
|
||||
TOY_LITERAL_FLOAT,
|
||||
TOY_LITERAL_STRING,
|
||||
TOY_LITERAL_ARRAY,
|
||||
TOY_LITERAL_DICTIONARY,
|
||||
TOY_LITERAL_FUNCTION,
|
||||
TOY_LITERAL_IDENTIFIER,
|
||||
TOY_LITERAL_TYPE,
|
||||
TOY_LITERAL_OPAQUE,
|
||||
TOY_LITERAL_ANY,
|
||||
|
||||
//these are meta-level types - not for general use
|
||||
TOY_LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
|
||||
TOY_LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
|
||||
TOY_LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
|
||||
TOY_LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
|
||||
TOY_LITERAL_FUNCTION_NATIVE, //for handling native functions only
|
||||
TOY_LITERAL_INDEX_BLANK, //for blank indexing i.e. arr[:]
|
||||
} Toy_LiteralType;
|
||||
|
||||
typedef struct {
|
||||
Toy_LiteralType type;
|
||||
union {
|
||||
bool boolean;
|
||||
int integer;
|
||||
float number;
|
||||
struct {
|
||||
Toy_RefString* ptr;
|
||||
//string hash?
|
||||
} string;
|
||||
|
||||
void* array;
|
||||
void* dictionary;
|
||||
|
||||
struct {
|
||||
void* bytecode;
|
||||
void* scope;
|
||||
int length;
|
||||
} function;
|
||||
|
||||
struct { //for variable names
|
||||
Toy_RefString* ptr;
|
||||
int hash;
|
||||
} identifier;
|
||||
|
||||
struct {
|
||||
Toy_LiteralType typeOf; //no longer a mask
|
||||
bool constant;
|
||||
void* subtypes; //for nested types caused by compounds
|
||||
int capacity;
|
||||
int count;
|
||||
} type;
|
||||
|
||||
struct {
|
||||
void* ptr;
|
||||
int tag; //TODO: remove tags?
|
||||
} opaque;
|
||||
} as;
|
||||
} Toy_Literal;
|
||||
|
||||
#define TOY_IS_NULL(value) ((value).type == TOY_LITERAL_NULL)
|
||||
#define TOY_IS_BOOLEAN(value) ((value).type == TOY_LITERAL_BOOLEAN)
|
||||
#define TOY_IS_INTEGER(value) ((value).type == TOY_LITERAL_INTEGER)
|
||||
#define TOY_IS_FLOAT(value) ((value).type == TOY_LITERAL_FLOAT)
|
||||
#define TOY_IS_STRING(value) ((value).type == TOY_LITERAL_STRING)
|
||||
#define TOY_IS_ARRAY(value) ((value).type == TOY_LITERAL_ARRAY)
|
||||
#define TOY_IS_DICTIONARY(value) ((value).type == TOY_LITERAL_DICTIONARY)
|
||||
#define TOY_IS_FUNCTION(value) ((value).type == TOY_LITERAL_FUNCTION)
|
||||
#define TOY_IS_FUNCTION_NATIVE(value) ((value).type == TOY_LITERAL_FUNCTION_NATIVE)
|
||||
#define TOY_IS_IDENTIFIER(value) ((value).type == TOY_LITERAL_IDENTIFIER)
|
||||
#define TOY_IS_TYPE(value) ((value).type == TOY_LITERAL_TYPE)
|
||||
#define TOY_IS_OPAQUE(value) ((value).type == TOY_LITERAL_OPAQUE)
|
||||
|
||||
#define TOY_AS_BOOLEAN(value) ((value).as.boolean)
|
||||
#define TOY_AS_INTEGER(value) ((value).as.integer)
|
||||
#define TOY_AS_FLOAT(value) ((value).as.number)
|
||||
#define TOY_AS_STRING(value) ((value).as.string.ptr)
|
||||
#define TOY_AS_ARRAY(value) ((Toy_LiteralArray*)((value).as.array))
|
||||
#define TOY_AS_DICTIONARY(value) ((Toy_LiteralDictionary*)((value).as.dictionary))
|
||||
#define TOY_AS_FUNCTION(value) ((value).as.function)
|
||||
#define TOY_AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||
#define TOY_AS_TYPE(value) ((value).as.type)
|
||||
#define TOY_AS_OPAQUE(value) ((value).as.opaque.ptr)
|
||||
|
||||
#define TOY_TO_NULL_LITERAL ((Toy_Literal){TOY_LITERAL_NULL, { .integer = 0 }})
|
||||
#define TOY_TO_BOOLEAN_LITERAL(value) ((Toy_Literal){TOY_LITERAL_BOOLEAN, { .boolean = value }})
|
||||
#define TOY_TO_INTEGER_LITERAL(value) ((Toy_Literal){TOY_LITERAL_INTEGER, { .integer = value }})
|
||||
#define TOY_TO_FLOAT_LITERAL(value) ((Toy_Literal){TOY_LITERAL_FLOAT, { .number = value }})
|
||||
#define TOY_TO_STRING_LITERAL(value) Toy_private_toStringLiteral(value)
|
||||
#define TOY_TO_ARRAY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_ARRAY, { .array = value }})
|
||||
#define TOY_TO_DICTIONARY_LITERAL(value) ((Toy_Literal){TOY_LITERAL_DICTIONARY, { .dictionary = value }})
|
||||
#define TOY_TO_FUNCTION_LITERAL(value, l) ((Toy_Literal){TOY_LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
||||
#define TOY_TO_IDENTIFIER_LITERAL(value) Toy_private_toIdentifierLiteral(value)
|
||||
#define TOY_TO_TYPE_LITERAL(value, c) ((Toy_Literal){ TOY_LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
||||
#define TOY_TO_OPAQUE_LITERAL(value, t) ((Toy_Literal){ TOY_LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
|
||||
|
||||
//BUGFIX: For blank indexing
|
||||
#define TOY_IS_INDEX_BLANK(value) ((value).type == TOY_LITERAL_INDEX_BLANK)
|
||||
#define TOY_TO_INDEX_BLANK_LITERAL ((Toy_Literal){TOY_LITERAL_INDEX_BLANK, { .integer = 0 }})
|
||||
|
||||
TOY_API void Toy_freeLiteral(Toy_Literal literal);
|
||||
|
||||
#define TOY_IS_TRUTHY(x) Toy_private_isTruthy(x)
|
||||
|
||||
#define TOY_MAX_STRING_LENGTH 4096
|
||||
#define TOY_HASH_I(lit) ((lit).as.identifier.hash)
|
||||
#define TOY_TYPE_PUSH_SUBTYPE(lit, subtype) Toy_private_typePushSubtype(lit, subtype)
|
||||
#define TOY_GET_OPAQUE_TAG(o) o.as.opaque.tag
|
||||
|
||||
//BUGFIX: macros are not functions
|
||||
TOY_API bool Toy_private_isTruthy(Toy_Literal x);
|
||||
TOY_API Toy_Literal Toy_private_toStringLiteral(Toy_RefString* ptr);
|
||||
TOY_API Toy_Literal Toy_private_toIdentifierLiteral(Toy_RefString* ptr);
|
||||
TOY_API Toy_Literal* Toy_private_typePushSubtype(Toy_Literal* lit, Toy_Literal subtype);
|
||||
|
||||
//utils
|
||||
TOY_API Toy_Literal Toy_copyLiteral(Toy_Literal original);
|
||||
TOY_API bool Toy_literalsAreEqual(Toy_Literal lhs, Toy_Literal rhs);
|
||||
TOY_API int Toy_hashLiteral(Toy_Literal lit);
|
||||
|
||||
TOY_API void Toy_printLiteral(Toy_Literal literal);
|
||||
TOY_API void Toy_printLiteralCustom(Toy_Literal literal, void (printFn)(const char*));
|
||||
@@ -1,98 +0,0 @@
|
||||
#include "toy_literal_array.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLiteralArray(Toy_LiteralArray* array) {
|
||||
array->capacity = 0;
|
||||
array->count = 0;
|
||||
array->literals = NULL;
|
||||
}
|
||||
|
||||
void Toy_freeLiteralArray(Toy_LiteralArray* array) {
|
||||
//clean up memory
|
||||
for(int i = 0; i < array->count; i++) {
|
||||
Toy_freeLiteral(array->literals[i]);
|
||||
}
|
||||
|
||||
TOY_FREE_ARRAY(Toy_Literal, array->literals, array->capacity);
|
||||
Toy_initLiteralArray(array);
|
||||
}
|
||||
|
||||
int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||
if (array->capacity < array->count + 1) {
|
||||
int oldCapacity = array->capacity;
|
||||
|
||||
array->capacity = TOY_GROW_CAPACITY(oldCapacity);
|
||||
array->literals = TOY_GROW_ARRAY(Toy_Literal, array->literals, oldCapacity, array->capacity);
|
||||
}
|
||||
|
||||
array->literals[array->count] = Toy_copyLiteral(literal);
|
||||
return array->count++;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array) {
|
||||
if (array->count <= 0) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//get the return
|
||||
Toy_Literal ret = array->literals[array->count-1];
|
||||
|
||||
//null the existing data
|
||||
array->literals[array->count-1] = TOY_TO_NULL_LITERAL;
|
||||
|
||||
array->count--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//find a literal in the array that matches the "literal" argument
|
||||
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal) {
|
||||
for (int i = 0; i < array->count; i++) {
|
||||
//not the same type
|
||||
if (array->literals[i].type != literal.type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//types match?
|
||||
if (Toy_literalsAreEqual(array->literals[i], literal)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value) {
|
||||
if (!TOY_IS_INTEGER(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = TOY_AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Toy_freeLiteral(array->literals[idx]);
|
||||
array->literals[idx] = Toy_copyLiteral(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index) {
|
||||
if (!TOY_IS_INTEGER(index)) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
int idx = TOY_AS_INTEGER(index);
|
||||
|
||||
if (idx < 0 || idx >= array->count) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
return Toy_copyLiteral(array->literals[idx]);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
|
||||
typedef struct Toy_LiteralArray {
|
||||
Toy_Literal* literals;
|
||||
int capacity;
|
||||
int count;
|
||||
} Toy_LiteralArray;
|
||||
|
||||
TOY_API void Toy_initLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API void Toy_freeLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API int Toy_pushLiteralArray(Toy_LiteralArray* array, Toy_Literal literal);
|
||||
TOY_API Toy_Literal Toy_popLiteralArray(Toy_LiteralArray* array);
|
||||
TOY_API bool Toy_setLiteralArray(Toy_LiteralArray* array, Toy_Literal index, Toy_Literal value);
|
||||
TOY_API Toy_Literal Toy_getLiteralArray(Toy_LiteralArray* array, Toy_Literal index);
|
||||
|
||||
int Toy_findLiteralIndex(Toy_LiteralArray* array, Toy_Literal literal);
|
||||
@@ -1,219 +0,0 @@
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//util functions
|
||||
static void setEntryValues(Toy_private_entry* entry, Toy_Literal key, Toy_Literal value) {
|
||||
//much simpler now
|
||||
Toy_freeLiteral(entry->key);
|
||||
entry->key = Toy_copyLiteral(key);
|
||||
|
||||
Toy_freeLiteral(entry->value);
|
||||
entry->value = Toy_copyLiteral(value);
|
||||
}
|
||||
|
||||
static Toy_private_entry* getEntryArray(Toy_private_entry* array, int capacity, Toy_Literal key, unsigned int hash, bool mustExist) {
|
||||
//find "key", starting at index
|
||||
unsigned int index = hash % capacity;
|
||||
unsigned int start = index;
|
||||
|
||||
//increment once, so it can't equal start
|
||||
index = (index + 1) % capacity;
|
||||
|
||||
//literal probing and collision checking
|
||||
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
|
||||
Toy_private_entry* entry = &array[index];
|
||||
|
||||
if (TOY_IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
|
||||
if (TOY_IS_NULL(entry->value) && !mustExist) {
|
||||
//found a truly empty bucket
|
||||
return entry;
|
||||
}
|
||||
//else it's a tombstone - ignore
|
||||
} else {
|
||||
if (Toy_literalsAreEqual(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
index = (index + 1) % capacity;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void adjustEntryCapacity(Toy_private_entry** dictionaryHandle, int oldCapacity, int capacity) {
|
||||
//new entry space
|
||||
Toy_private_entry* newEntries = TOY_ALLOCATE(Toy_private_entry, capacity);
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
newEntries[i].key = TOY_TO_NULL_LITERAL;
|
||||
newEntries[i].value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//move the old array into the new one
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
if (TOY_IS_NULL((*dictionaryHandle)[i].key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//place the key and value in the new array (reusing string memory)
|
||||
Toy_private_entry* entry = getEntryArray(newEntries, capacity, TOY_TO_NULL_LITERAL, Toy_hashLiteral((*dictionaryHandle)[i].key), false);
|
||||
|
||||
entry->key = (*dictionaryHandle)[i].key;
|
||||
entry->value = (*dictionaryHandle)[i].value;
|
||||
}
|
||||
|
||||
//clear the old array
|
||||
TOY_FREE_ARRAY(Toy_private_entry, *dictionaryHandle, oldCapacity);
|
||||
|
||||
*dictionaryHandle = newEntries;
|
||||
}
|
||||
|
||||
static bool setEntryArray(Toy_private_entry** dictionaryHandle, int* capacityPtr, int contains, Toy_Literal key, Toy_Literal value, int hash) {
|
||||
//expand array if needed
|
||||
if (contains + 1 > *capacityPtr * TOY_DICTIONARY_MAX_LOAD) {
|
||||
int oldCapacity = *capacityPtr;
|
||||
*capacityPtr = TOY_GROW_CAPACITY(*capacityPtr);
|
||||
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
|
||||
}
|
||||
|
||||
Toy_private_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
|
||||
|
||||
//true = contains increase
|
||||
if (TOY_IS_NULL(entry->key)) {
|
||||
setEntryValues(entry, key, value);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
setEntryValues(entry, key, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void freeEntry(Toy_private_entry* entry) {
|
||||
Toy_freeLiteral(entry->key);
|
||||
Toy_freeLiteral(entry->value);
|
||||
entry->key = TOY_TO_NULL_LITERAL;
|
||||
entry->value = TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
static void freeEntryArray(Toy_private_entry* array, int capacity) {
|
||||
if (array == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (!TOY_IS_NULL(array[i].key)) {
|
||||
freeEntry(&array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TOY_FREE_ARRAY(Toy_private_entry, array, capacity);
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
|
||||
dictionary->entries = NULL;
|
||||
dictionary->capacity = TOY_GROW_CAPACITY(0);
|
||||
dictionary->contains = 0;
|
||||
dictionary->count = 0;
|
||||
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
|
||||
}
|
||||
|
||||
void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary) {
|
||||
freeEntryArray(dictionary->entries, dictionary->capacity);
|
||||
dictionary->capacity = 0;
|
||||
dictionary->contains = 0;
|
||||
}
|
||||
|
||||
void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (set)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, Toy_hashLiteral(key));
|
||||
|
||||
if (increment) {
|
||||
dictionary->contains++;
|
||||
dictionary->count++;
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (get)\n" TOY_CC_RESET);
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
return Toy_copyLiteral(entry->value);
|
||||
}
|
||||
else {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
if (TOY_IS_NULL(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have null keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
//BUGFIX: Can't hash a function
|
||||
if (TOY_IS_FUNCTION(key) || TOY_IS_FUNCTION_NATIVE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have function keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TOY_IS_OPAQUE(key)) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Dictionaries can't have opaque keys (remove)\n" TOY_CC_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), true);
|
||||
|
||||
if (entry != NULL) {
|
||||
freeEntry(entry);
|
||||
entry->value = TOY_TO_BOOLEAN_LITERAL(true); //tombstone
|
||||
dictionary->count--;
|
||||
}
|
||||
}
|
||||
|
||||
bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key) {
|
||||
//null & not tombstoned
|
||||
Toy_private_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, Toy_hashLiteral(key), false);
|
||||
return !(TOY_IS_NULL(entry->key) && TOY_IS_NULL(entry->value));
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_literal.h"
|
||||
|
||||
//TODO: benchmark this
|
||||
#define TOY_DICTIONARY_MAX_LOAD 0.75
|
||||
|
||||
typedef struct Toy_private_entry {
|
||||
Toy_Literal key;
|
||||
Toy_Literal value;
|
||||
} Toy_private_entry;
|
||||
|
||||
typedef struct Toy_LiteralDictionary {
|
||||
Toy_private_entry* entries;
|
||||
int capacity;
|
||||
int count;
|
||||
int contains; //count + tombstones, for internal use
|
||||
} Toy_LiteralDictionary;
|
||||
|
||||
TOY_API void Toy_initLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||
TOY_API void Toy_freeLiteralDictionary(Toy_LiteralDictionary* dictionary);
|
||||
|
||||
TOY_API void Toy_setLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key, Toy_Literal value);
|
||||
TOY_API Toy_Literal Toy_getLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
TOY_API void Toy_removeLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
|
||||
TOY_API bool Toy_existsLiteralDictionary(Toy_LiteralDictionary* dictionary, Toy_Literal key);
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "toy_memory.h"
|
||||
#include "toy_refstring.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//default allocator
|
||||
static void* defaultMemoryAllocator(void* pointer, size_t oldSize, size_t newSize) {
|
||||
if (newSize == 0 && oldSize == 0) {
|
||||
//causes issues, so just skip out with a NO-OP
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (newSize == 0) {
|
||||
free(pointer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* mem = realloc(pointer, newSize);
|
||||
|
||||
if (mem == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocation error (requested %d for %ld, replacing %d)\n" TOY_CC_RESET, (int)newSize, (long int)pointer, (int)oldSize);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
//static variables
|
||||
static Toy_MemoryAllocatorFn allocator;
|
||||
|
||||
//preload
|
||||
static void __attribute__((constructor)) preloadMemoryAllocator() {
|
||||
Toy_setMemoryAllocator(defaultMemoryAllocator);
|
||||
}
|
||||
|
||||
//exposed API
|
||||
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||
return allocator(pointer, oldSize, newSize);
|
||||
}
|
||||
|
||||
void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn fn) {
|
||||
if (fn == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't be null)\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fn == Toy_reallocate) {
|
||||
fprintf(stderr, TOY_CC_ERROR "[internal] Memory allocator error (can't loop the Toy_reallocate function)\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
allocator = fn;
|
||||
Toy_setRefStringAllocatorFn(fn);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#define TOY_ALLOCATE(type, count) ((type*)Toy_reallocate(NULL, 0, sizeof(type) * (count)))
|
||||
#define TOY_FREE(type, pointer) Toy_reallocate(pointer, sizeof(type), 0)
|
||||
#define TOY_GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||
#define TOY_GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
|
||||
#define TOY_GROW_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) (type*)Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define TOY_FREE_ARRAY(type, pointer, oldCount) Toy_reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||
|
||||
//implementation details
|
||||
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||
|
||||
//assign the memory allocator
|
||||
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
||||
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum Toy_Opcode {
|
||||
TOY_OP_EOF,
|
||||
|
||||
//basic statements
|
||||
TOY_OP_ASSERT,
|
||||
TOY_OP_PRINT,
|
||||
|
||||
//data
|
||||
TOY_OP_LITERAL,
|
||||
TOY_OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
||||
TOY_OP_LITERAL_RAW, //forcibly get the raw value of the literal
|
||||
|
||||
//arithmetic operators
|
||||
TOY_OP_NEGATE,
|
||||
TOY_OP_ADDITION,
|
||||
TOY_OP_SUBTRACTION,
|
||||
TOY_OP_MULTIPLICATION,
|
||||
TOY_OP_DIVISION,
|
||||
TOY_OP_MODULO,
|
||||
TOY_OP_GROUPING_BEGIN,
|
||||
TOY_OP_GROUPING_END,
|
||||
|
||||
//variable stuff
|
||||
TOY_OP_SCOPE_BEGIN,
|
||||
TOY_OP_SCOPE_END,
|
||||
|
||||
TOY_OP_TYPE_DECL, //declare a type to be used (as a literal)
|
||||
TOY_OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
||||
|
||||
TOY_OP_VAR_DECL, //declare a variable to be used (as a literal)
|
||||
TOY_OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
||||
|
||||
TOY_OP_FN_DECL, //declare a function to be used (as a literal)
|
||||
TOY_OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
|
||||
|
||||
TOY_OP_VAR_ASSIGN, //assign to a literal
|
||||
TOY_OP_VAR_ADDITION_ASSIGN,
|
||||
TOY_OP_VAR_SUBTRACTION_ASSIGN,
|
||||
TOY_OP_VAR_MULTIPLICATION_ASSIGN,
|
||||
TOY_OP_VAR_DIVISION_ASSIGN,
|
||||
TOY_OP_VAR_MODULO_ASSIGN,
|
||||
|
||||
TOY_OP_TYPE_CAST, //temporarily change a type of an atomic value
|
||||
TOY_OP_TYPE_OF, //get the type of a variable
|
||||
|
||||
TOY_OP_IMPORT,
|
||||
TOY_OP_EXPORT_removed,
|
||||
|
||||
//for indexing
|
||||
TOY_OP_INDEX,
|
||||
TOY_OP_INDEX_ASSIGN,
|
||||
TOY_OP_INDEX_ASSIGN_INTERMEDIATE,
|
||||
TOY_OP_DOT,
|
||||
|
||||
//comparison of values
|
||||
TOY_OP_COMPARE_EQUAL,
|
||||
TOY_OP_COMPARE_NOT_EQUAL,
|
||||
TOY_OP_COMPARE_LESS,
|
||||
TOY_OP_COMPARE_LESS_EQUAL,
|
||||
TOY_OP_COMPARE_GREATER,
|
||||
TOY_OP_COMPARE_GREATER_EQUAL,
|
||||
TOY_OP_INVERT, //for booleans
|
||||
|
||||
//logical operators
|
||||
TOY_OP_AND,
|
||||
TOY_OP_OR,
|
||||
|
||||
//jumps, and conditional jumps (absolute)
|
||||
TOY_OP_JUMP,
|
||||
TOY_OP_IF_FALSE_JUMP,
|
||||
TOY_OP_FN_CALL,
|
||||
TOY_OP_FN_RETURN,
|
||||
|
||||
//pop the stack at the end of a complex statement
|
||||
TOY_OP_POP_STACK,
|
||||
|
||||
//ternary shorthand
|
||||
TOY_OP_TERNARY,
|
||||
|
||||
//meta
|
||||
TOY_OP_FN_END, //different from SECTION_END
|
||||
TOY_OP_SECTION_END = 255,
|
||||
//TODO: add more
|
||||
} Toy_Opcode;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_common.h"
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_ast_node.h"
|
||||
|
||||
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
|
||||
typedef struct {
|
||||
Toy_Lexer* lexer;
|
||||
bool error; //I've had an error
|
||||
bool panic; //I am processing an error
|
||||
|
||||
//track the last two outputs from the lexer
|
||||
Toy_Token current;
|
||||
Toy_Token previous;
|
||||
} Toy_Parser;
|
||||
|
||||
TOY_API void Toy_initParser(Toy_Parser* parser, Toy_Lexer* lexer);
|
||||
TOY_API void Toy_freeParser(Toy_Parser* parser);
|
||||
TOY_API Toy_ASTNode* Toy_scanParser(Toy_Parser* parser);
|
||||
@@ -1,98 +0,0 @@
|
||||
#include "toy_refstring.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//test variable sizes based on platform (safety)
|
||||
#define STATIC_ASSERT(test_for_true) static_assert((test_for_true), "(" #test_for_true ") failed")
|
||||
|
||||
STATIC_ASSERT(sizeof(Toy_RefString) == 12);
|
||||
STATIC_ASSERT(sizeof(int) == 4);
|
||||
STATIC_ASSERT(sizeof(char) == 1);
|
||||
|
||||
//memory allocation
|
||||
static Toy_RefStringAllocatorFn allocate;
|
||||
|
||||
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn allocator) {
|
||||
allocate = allocator;
|
||||
}
|
||||
|
||||
//API
|
||||
Toy_RefString* Toy_createRefString(char* cstring) {
|
||||
int length = strnlen(cstring, 4096);
|
||||
|
||||
return Toy_createRefStringLength(cstring, length);
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_createRefStringLength(char* cstring, int length) {
|
||||
//allocate the memory area (including metadata space)
|
||||
Toy_RefString* refString = (Toy_RefString*)allocate(NULL, 0, sizeof(int) * 2 + sizeof(char) * length + 1);
|
||||
|
||||
//set the data
|
||||
refString->refcount = 1;
|
||||
refString->length = length;
|
||||
strncpy(refString->data, cstring, refString->length);
|
||||
|
||||
refString->data[refString->length] = '\0'; //string terminator
|
||||
|
||||
return refString;
|
||||
}
|
||||
|
||||
void Toy_deleteRefString(Toy_RefString* refString) {
|
||||
//decrement, then check
|
||||
refString->refcount--;
|
||||
if (refString->refcount <= 0) {
|
||||
allocate(refString, sizeof(int) * 2 + sizeof(char) * refString->length + 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int Toy_countRefString(Toy_RefString* refString) {
|
||||
return refString->refcount;
|
||||
}
|
||||
|
||||
int Toy_lengthRefString(Toy_RefString* refString) {
|
||||
return refString->length;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_copyRefString(Toy_RefString* refString) {
|
||||
//Cheaty McCheater Face
|
||||
refString->refcount++;
|
||||
return refString;
|
||||
}
|
||||
|
||||
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString) {
|
||||
//create a new string, with a new refcount
|
||||
return Toy_createRefStringLength(refString->data, refString->length);
|
||||
}
|
||||
|
||||
char* Toy_toCString(Toy_RefString* refString) {
|
||||
return refString->data;
|
||||
}
|
||||
|
||||
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs) {
|
||||
//same pointer
|
||||
if (lhs == rhs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//different length
|
||||
if (lhs->length != rhs->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, rhs->data, lhs->length) == 0;
|
||||
}
|
||||
|
||||
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring) {
|
||||
//get the rhs length
|
||||
int length = strnlen(cstring, 4096);
|
||||
|
||||
//different length
|
||||
if (lhs->length != length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//same string
|
||||
return strncmp(lhs->data, cstring, lhs->length) == 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//memory allocation hook
|
||||
typedef void* (*Toy_RefStringAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||
void Toy_setRefStringAllocatorFn(Toy_RefStringAllocatorFn);
|
||||
|
||||
//the RefString structure
|
||||
typedef struct Toy_RefString {
|
||||
int refcount;
|
||||
int length;
|
||||
char data[1];
|
||||
} Toy_RefString;
|
||||
|
||||
//API
|
||||
Toy_RefString* Toy_createRefString(char* cstring);
|
||||
Toy_RefString* Toy_createRefStringLength(char* cstring, int length);
|
||||
void Toy_deleteRefString(Toy_RefString* refString);
|
||||
int Toy_countRefString(Toy_RefString* refString);
|
||||
int Toy_lengthRefString(Toy_RefString* refString);
|
||||
Toy_RefString* Toy_copyRefString(Toy_RefString* refString);
|
||||
Toy_RefString* Toy_deepCopyRefString(Toy_RefString* refString);
|
||||
char* Toy_toCString(Toy_RefString* refString);
|
||||
bool Toy_equalsRefString(Toy_RefString* lhs, Toy_RefString* rhs);
|
||||
bool Toy_equalsRefStringCString(Toy_RefString* lhs, char* cstring);
|
||||
@@ -1,324 +0,0 @@
|
||||
#include "toy_scope.h"
|
||||
|
||||
#include "toy_memory.h"
|
||||
|
||||
//run up the ancestor chain, freeing anything with 0 references left
|
||||
static void freeAncestorChain(Toy_Scope* scope) {
|
||||
scope->references--;
|
||||
|
||||
//free scope chain
|
||||
if (scope->ancestor != NULL) {
|
||||
freeAncestorChain(scope->ancestor);
|
||||
}
|
||||
|
||||
if (scope->references > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_freeLiteralDictionary(&scope->variables);
|
||||
Toy_freeLiteralDictionary(&scope->types);
|
||||
|
||||
TOY_FREE(Toy_Scope, scope);
|
||||
}
|
||||
|
||||
//return false if invalid type
|
||||
static bool checkType(Toy_Literal typeLiteral, Toy_Literal original, Toy_Literal value, bool constCheck) {
|
||||
//for constants, fail if original != value
|
||||
if (constCheck && TOY_AS_TYPE(typeLiteral).constant && !Toy_literalsAreEqual(original, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//for any types
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ANY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//don't allow null types
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//always allow null values
|
||||
if (TOY_IS_NULL(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//for each type, if a mismatch is found, return false
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_BOOLEAN && !TOY_IS_BOOLEAN(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_INTEGER && !TOY_IS_INTEGER(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FLOAT && !TOY_IS_FLOAT(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_STRING && !TOY_IS_STRING(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_ARRAY && !TOY_IS_ARRAY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_ARRAY(value)) {
|
||||
//check value's type
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new array variable that needs checking
|
||||
if (TOY_IS_NULL(original)) {
|
||||
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
|
||||
if (!checkType( ((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check children
|
||||
for (int i = 0; i < TOY_AS_ARRAY(value)->count; i++) {
|
||||
if (TOY_AS_ARRAY(original)->count <= i) {
|
||||
return true; //assume new entry pushed
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_AS_ARRAY(original)->literals[i], TOY_AS_ARRAY(value)->literals[i], constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_DICTIONARY && !TOY_IS_DICTIONARY(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_IS_DICTIONARY(value)) {
|
||||
//check value's type
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf != TOY_LITERAL_DICTIONARY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if null, assume it's a new dictionary variable that needs checking
|
||||
if (TOY_IS_NULL(original)) {
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
|
||||
//check the type of key and value
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], TOY_TO_NULL_LITERAL, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//check each child of value against the child of original
|
||||
for (int i = 0; i < TOY_AS_DICTIONARY(value)->capacity; i++) {
|
||||
if (TOY_IS_NULL(TOY_AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
|
||||
continue;
|
||||
}
|
||||
|
||||
//find the internal child of original that matches this child of value
|
||||
Toy_private_entry* ptr = NULL;
|
||||
|
||||
for (int j = 0; j < TOY_AS_DICTIONARY(original)->capacity; j++) {
|
||||
if (Toy_literalsAreEqual(TOY_AS_DICTIONARY(original)->entries[j].key, TOY_AS_DICTIONARY(value)->entries[i].key)) {
|
||||
ptr = &TOY_AS_DICTIONARY(original)->entries[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if not found, assume it's a new entry
|
||||
if (!ptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//check the type of key and value
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[0], ptr->key, TOY_AS_DICTIONARY(value)->entries[i].key, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkType(((Toy_Literal*)(TOY_AS_TYPE(typeLiteral).subtypes))[1], ptr->value, TOY_AS_DICTIONARY(value)->entries[i].value, constCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_FUNCTION && !TOY_IS_FUNCTION(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TOY_AS_TYPE(typeLiteral).typeOf == TOY_LITERAL_TYPE && !TOY_IS_TYPE(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
Toy_Scope* Toy_pushScope(Toy_Scope* ancestor) {
|
||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||
scope->ancestor = ancestor;
|
||||
Toy_initLiteralDictionary(&scope->variables);
|
||||
Toy_initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
||||
if (scope == NULL) { //CAN pop a null
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Toy_Scope* ret = scope->ancestor;
|
||||
|
||||
//BUGFIX: when freeing a scope, free the function's scopes manually
|
||||
for (int i = 0; i < scope->variables.capacity; i++) {
|
||||
//handle keys, just in case
|
||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].key)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].key).scope);
|
||||
TOY_AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
|
||||
}
|
||||
|
||||
if (TOY_IS_FUNCTION(scope->variables.entries[i].value)) {
|
||||
Toy_popScope(TOY_AS_FUNCTION(scope->variables.entries[i].value).scope);
|
||||
TOY_AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
freeAncestorChain(scope);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Toy_Scope* Toy_copyScope(Toy_Scope* original) {
|
||||
Toy_Scope* scope = TOY_ALLOCATE(Toy_Scope, 1);
|
||||
scope->ancestor = original->ancestor;
|
||||
Toy_initLiteralDictionary(&scope->variables);
|
||||
Toy_initLiteralDictionary(&scope->types);
|
||||
|
||||
//tick up all scope reference counts
|
||||
scope->references = 0;
|
||||
for (Toy_Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
||||
ptr->references++;
|
||||
}
|
||||
|
||||
//copy the contents of the dictionaries
|
||||
for (int i = 0; i < original->variables.capacity; i++) {
|
||||
if (!TOY_IS_NULL(original->variables.entries[i].key)) {
|
||||
Toy_setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < original->types.capacity; i++) {
|
||||
if (!TOY_IS_NULL(original->types.entries[i].key)) {
|
||||
Toy_setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
//returns false if error
|
||||
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type) {
|
||||
//don't redefine a variable within this scope
|
||||
if (Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TOY_IS_TYPE(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//store the type, for later checking on assignment
|
||||
Toy_setLiteralDictionary(&scope->types, key, type);
|
||||
|
||||
Toy_setLiteralDictionary(&scope->variables, key, TOY_TO_NULL_LITERAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key) {
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return Toy_isDelcaredScopeVariable(scope->ancestor, key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//return false if undefined, or can't be assigned
|
||||
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return Toy_setScopeVariable(scope->ancestor, key, value, constCheck);
|
||||
}
|
||||
|
||||
//type checking
|
||||
Toy_Literal typeLiteral = Toy_getLiteralDictionary(&scope->types, key);
|
||||
Toy_Literal original = Toy_getLiteralDictionary(&scope->variables, key);
|
||||
|
||||
if (!checkType(typeLiteral, original, value, constCheck)) {
|
||||
Toy_freeLiteral(typeLiteral);
|
||||
Toy_freeLiteral(original);
|
||||
return false;
|
||||
}
|
||||
|
||||
//actually assign
|
||||
Toy_setLiteralDictionary(&scope->variables, key, value);
|
||||
|
||||
Toy_freeLiteral(typeLiteral);
|
||||
Toy_freeLiteral(original);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* valueHandle) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!Toy_existsLiteralDictionary(&scope->variables, key)) {
|
||||
return Toy_getScopeVariable(scope->ancestor, key, valueHandle);
|
||||
}
|
||||
|
||||
*valueHandle = Toy_getLiteralDictionary(&scope->variables, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key) {
|
||||
//dead end
|
||||
if (scope == NULL) {
|
||||
return TOY_TO_NULL_LITERAL;
|
||||
}
|
||||
|
||||
//if it's not in this scope, keep searching up the chain
|
||||
if (!Toy_existsLiteralDictionary(&scope->types, key)) {
|
||||
return Toy_getScopeType(scope->ancestor, key);
|
||||
}
|
||||
|
||||
return Toy_getLiteralDictionary(&scope->types, key);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_literal_array.h"
|
||||
#include "toy_literal_dictionary.h"
|
||||
|
||||
typedef struct Toy_Scope {
|
||||
Toy_LiteralDictionary variables; //only allow identifiers as the keys
|
||||
Toy_LiteralDictionary types; //the types, indexed by identifiers
|
||||
struct Toy_Scope* ancestor;
|
||||
int references; //how many scopes point here
|
||||
} Toy_Scope;
|
||||
|
||||
Toy_Scope* Toy_pushScope(Toy_Scope* scope);
|
||||
Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||
Toy_Scope* Toy_copyScope(Toy_Scope* original);
|
||||
|
||||
//returns false if error
|
||||
bool Toy_declareScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal type);
|
||||
bool Toy_isDelcaredScopeVariable(Toy_Scope* scope, Toy_Literal key);
|
||||
|
||||
//return false if undefined
|
||||
bool Toy_setScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal value, bool constCheck);
|
||||
bool Toy_getScopeVariable(Toy_Scope* scope, Toy_Literal key, Toy_Literal* value);
|
||||
|
||||
Toy_Literal Toy_getScopeType(Toy_Scope* scope, Toy_Literal key);
|
||||
@@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum Toy_TokenType {
|
||||
//types
|
||||
TOY_TOKEN_NULL,
|
||||
TOY_TOKEN_BOOLEAN,
|
||||
TOY_TOKEN_INTEGER,
|
||||
TOY_TOKEN_FLOAT,
|
||||
TOY_TOKEN_STRING,
|
||||
TOY_TOKEN_ARRAY,
|
||||
TOY_TOKEN_DICTIONARY,
|
||||
TOY_TOKEN_FUNCTION,
|
||||
TOY_TOKEN_OPAQUE,
|
||||
TOY_TOKEN_ANY,
|
||||
|
||||
//keywords and reserved words
|
||||
TOY_TOKEN_AS,
|
||||
TOY_TOKEN_ASSERT,
|
||||
TOY_TOKEN_BREAK,
|
||||
TOY_TOKEN_CLASS,
|
||||
TOY_TOKEN_CONST,
|
||||
TOY_TOKEN_CONTINUE,
|
||||
TOY_TOKEN_DO,
|
||||
TOY_TOKEN_ELSE,
|
||||
TOY_TOKEN_EXPORT,
|
||||
TOY_TOKEN_FOR,
|
||||
TOY_TOKEN_FOREACH,
|
||||
TOY_TOKEN_IF,
|
||||
TOY_TOKEN_IMPORT,
|
||||
TOY_TOKEN_IN,
|
||||
TOY_TOKEN_OF,
|
||||
TOY_TOKEN_PRINT,
|
||||
TOY_TOKEN_RETURN,
|
||||
TOY_TOKEN_TYPE,
|
||||
TOY_TOKEN_ASTYPE,
|
||||
TOY_TOKEN_TYPEOF,
|
||||
TOY_TOKEN_VAR,
|
||||
TOY_TOKEN_WHILE,
|
||||
|
||||
//literal values
|
||||
TOY_TOKEN_IDENTIFIER,
|
||||
TOY_TOKEN_LITERAL_TRUE,
|
||||
TOY_TOKEN_LITERAL_FALSE,
|
||||
TOY_TOKEN_LITERAL_INTEGER,
|
||||
TOY_TOKEN_LITERAL_FLOAT,
|
||||
TOY_TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
TOY_TOKEN_PLUS,
|
||||
TOY_TOKEN_MINUS,
|
||||
TOY_TOKEN_MULTIPLY,
|
||||
TOY_TOKEN_DIVIDE,
|
||||
TOY_TOKEN_MODULO,
|
||||
TOY_TOKEN_PLUS_ASSIGN,
|
||||
TOY_TOKEN_MINUS_ASSIGN,
|
||||
TOY_TOKEN_MULTIPLY_ASSIGN,
|
||||
TOY_TOKEN_DIVIDE_ASSIGN,
|
||||
TOY_TOKEN_MODULO_ASSIGN,
|
||||
TOY_TOKEN_PLUS_PLUS,
|
||||
TOY_TOKEN_MINUS_MINUS,
|
||||
TOY_TOKEN_ASSIGN,
|
||||
|
||||
//logical operators
|
||||
TOY_TOKEN_PAREN_LEFT,
|
||||
TOY_TOKEN_PAREN_RIGHT,
|
||||
TOY_TOKEN_BRACKET_LEFT,
|
||||
TOY_TOKEN_BRACKET_RIGHT,
|
||||
TOY_TOKEN_BRACE_LEFT,
|
||||
TOY_TOKEN_BRACE_RIGHT,
|
||||
TOY_TOKEN_NOT,
|
||||
TOY_TOKEN_NOT_EQUAL,
|
||||
TOY_TOKEN_EQUAL,
|
||||
TOY_TOKEN_LESS,
|
||||
TOY_TOKEN_GREATER,
|
||||
TOY_TOKEN_LESS_EQUAL,
|
||||
TOY_TOKEN_GREATER_EQUAL,
|
||||
TOY_TOKEN_AND,
|
||||
TOY_TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOY_TOKEN_QUESTION,
|
||||
TOY_TOKEN_COLON,
|
||||
TOY_TOKEN_SEMICOLON,
|
||||
TOY_TOKEN_COMMA,
|
||||
TOY_TOKEN_DOT,
|
||||
TOY_TOKEN_PIPE,
|
||||
TOY_TOKEN_REST,
|
||||
|
||||
//meta tokens
|
||||
TOY_TOKEN_PASS,
|
||||
TOY_TOKEN_ERROR,
|
||||
TOY_TOKEN_EOF,
|
||||
} Toy_TokenType;
|
||||
@@ -1,37 +0,0 @@
|
||||
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) ../repl/repl_tools.c
|
||||
TESTS = $(wildcard test_*.c)
|
||||
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
|
||||
|
||||
.PRECIOUS: $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
||||
|
||||
all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
|
||||
|
||||
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
|
||||
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
|
||||
ifeq ($(shell uname)$(DISABLE_VALGRIND),Linux)
|
||||
valgrind --leak-check=full --track-origins=yes $@
|
||||
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)
|
||||
@@ -1,31 +0,0 @@
|
||||
//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";
|
||||
@@ -1,23 +0,0 @@
|
||||
//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";
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
//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";
|
||||
@@ -1,13 +0,0 @@
|
||||
//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";
|
||||
@@ -1,27 +0,0 @@
|
||||
//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";
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
//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";
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
|
||||
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";
|
||||
@@ -1,30 +0,0 @@
|
||||
//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";
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
fn _add(self, inc) {
|
||||
return self + inc;
|
||||
}
|
||||
|
||||
assert 1.add(2).add(3).add(4) == 10, "dottify bugfix failed";
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -1,77 +0,0 @@
|
||||
//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";
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
//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";
|
||||
@@ -1,43 +0,0 @@
|
||||
//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";
|
||||
@@ -1,40 +0,0 @@
|
||||
//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";
|
||||
@@ -1,87 +0,0 @@
|
||||
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();
|
||||
@@ -1,82 +0,0 @@
|
||||
//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";
|
||||
@@ -1,50 +0,0 @@
|
||||
//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";
|
||||
@@ -1,88 +0,0 @@
|
||||
import runner;
|
||||
|
||||
//test basic loading and freeing of a script file
|
||||
{
|
||||
var s = loadScript("scripts:/runner_sample_code.toy");
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test basic loading and freeing of a binary file
|
||||
{
|
||||
var s = loadScriptBytecode("scripts:/lib/runner/sample_bytecode.tb");
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test running an external script
|
||||
{
|
||||
var s = loadScript("scripts:/runner_sample_code.toy");
|
||||
|
||||
s.runScript();
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test running an external binary file
|
||||
{
|
||||
var s = loadScriptBytecode("scripts:/lib/runner/sample_bytecode.tb");
|
||||
|
||||
s.runScript();
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test resetting an external script
|
||||
{
|
||||
var s = loadScript("scripts:/runner_sample_code.toy");
|
||||
|
||||
s.runScript();
|
||||
s.resetScript();
|
||||
|
||||
assert !s.checkScriptDirty(), "checkScriptDirty failed";
|
||||
|
||||
s.runScript();
|
||||
|
||||
assert s.checkScriptDirty(), "_checkScriptDirty() failed";
|
||||
|
||||
s.resetScript();
|
||||
s.runScript();
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test running a nested external script
|
||||
{
|
||||
var s = loadScript("scripts:/lib/runner/sample_1.toy");
|
||||
|
||||
s.runScript();
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test retrieving a script variable
|
||||
{
|
||||
var s = loadScript("scripts:/runner_sample_code.toy");
|
||||
|
||||
s.runScript();
|
||||
|
||||
var fib = s.getScriptVar("fib");
|
||||
|
||||
assert fib(12) == 144, "_getScriptVar() failed";
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
//test calling a script function
|
||||
{
|
||||
var s = loadScript("scripts:/runner_sample_code.toy");
|
||||
|
||||
s.runScript();
|
||||
|
||||
assert s.callScriptFn("fib", 12) == 144, "_callScriptFn() failed";
|
||||
|
||||
s.freeScript();
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
@@ -1,9 +0,0 @@
|
||||
import runner;
|
||||
|
||||
//delegate to the other sample script
|
||||
var s = loadScript("scripts:/lib/runner/sample_2.toy");
|
||||
|
||||
s.runScript();
|
||||
|
||||
s.freeScript();
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
assert true, "Nested sample scripts worked";
|
||||
@@ -1,10 +0,0 @@
|
||||
//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";
|
||||
@@ -1,92 +0,0 @@
|
||||
//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();
|
||||
}
|
||||
|
||||
{
|
||||
//test positive and negative values of timers
|
||||
import timer;
|
||||
|
||||
var a = createTimer(1, 0);
|
||||
var b = createTimer(2, 0);
|
||||
|
||||
var acmp = a.compareTimer(b);
|
||||
var bcmp = b.compareTimer(a);
|
||||
|
||||
var c = createTimer(0, 1);
|
||||
var d = createTimer(0, 2);
|
||||
|
||||
var ccmp = c.compareTimer(d);
|
||||
var dcmp = d.compareTimer(c);
|
||||
|
||||
assert acmp.timerToString() == "1.000000", "positive and negative tests failed (acmp)";
|
||||
assert bcmp.timerToString() == "-1.000000", "positive and negative tests failed (bcmp)";
|
||||
assert ccmp.timerToString() == "0.000001", "positive and negative tests failed (ccmp)";
|
||||
assert dcmp.timerToString() == "-0.000001", "positive and negative tests failed (dcmp)";
|
||||
|
||||
a.destroyTimer();
|
||||
b.destroyTimer();
|
||||
c.destroyTimer();
|
||||
d.destroyTimer();
|
||||
acmp.destroyTimer();
|
||||
bcmp.destroyTimer();
|
||||
ccmp.destroyTimer();
|
||||
dcmp.destroyTimer();
|
||||
}
|
||||
|
||||
|
||||
print "All good";
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
assert true && true, "boolean and failed";
|
||||
assert true || true, "boolean or failed";
|
||||
|
||||
assert false || true && true, "boolen precedence failed";
|
||||
|
||||
print "All good";
|
||||
@@ -1,339 +0,0 @@
|
||||
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;
|
||||
@@ -1,4 +0,0 @@
|
||||
//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;
|
||||
@@ -1,335 +0,0 @@
|
||||
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;
|
||||
|
||||
//-------------------------
|
||||
@@ -1,8 +0,0 @@
|
||||
import runner;
|
||||
|
||||
var s = loadScript("scripts:/lib/../runner_sample_code.toy");
|
||||
|
||||
s.runScript();
|
||||
|
||||
s.freeScript();
|
||||
|
||||