Compare commits

..

51 Commits

Author SHA1 Message Date
Kayne Ruse 7f5e2a29eb Deprecated old v2 docs branch 2026-05-15 08:51:47 +10:00
Kayne Ruse 8b5a991ed1 Update index.md 2026-05-09 13:10:58 +10:00
Kayne Ruse 511c0280df Update index.md
Added info about my gitea.
2026-05-01 19:17:10 +10:00
Kayne Ruse 34e90879d7 Re-added twitter metatags 2026-04-11 01:18:29 +10:00
Kayne Ruse 08a417a66a Stripped back docs website 2026-04-10 12:32:52 +10:00
Kayne Ruse 60c07d64ef Fixed a typo 2025-03-01 16:21:59 +11:00
Kayne Ruse 0f4d474231 Tweaked repo link 2025-02-28 20:57:51 +11:00
Kayne Ruse a6fc01f7be Updated links after moving the repo 2025-02-28 20:11:03 +11:00
Kayne Ruse 9b68589d87 Swapping v1 and v2 sites 2025-02-28 11:42:53 +11:00
Kayne Ruse 3f0deaeb8a Squish da kitty 2025-02-18 00:04:00 +11:00
Kayne Ruse 5028f9a9f6 Invert blacktocat.png 2025-02-17 23:57:17 +11:00
Kayne Ruse 76437cb093 A better link to the repo 2025-02-17 23:53:22 +11:00
Kayne Ruse 92bc0466eb Added google analytics tag 2025-02-17 23:39:19 +11:00
Kayne Ruse 6eecfc31d6 Tweak 2025-02-02 22:42:57 +11:00
Kayne Ruse f7bab4da10 Tweaked wording 2025-02-02 22:33:11 +11:00
Kayne Ruse d95c6e6479 Oh fuck Jekyll. If this doesn't work, I'm leaving it for now. 2025-02-02 17:11:07 +11:00
Kayne Ruse 47e6983ad1 Jekyll is an ass. 2025-02-02 17:04:21 +11:00
Kayne Ruse 66e8c0fa7a ffs. 2025-02-02 17:00:55 +11:00
Kayne Ruse 2ea552fedd Fixed syntax include 2025-02-02 16:55:40 +11:00
Kayne Ruse 3747d0a326 Tweaked syntax highlighting, added control flow to docs
I also found some issues in the code, so I fixed them first.
2025-02-02 16:44:34 +11:00
Kayne Ruse c2a13e4183 Well, I clucked that up. 2025-01-24 17:15:58 +11:00
Kayne Ruse 062f676f52 Nothing to see here. 2025-01-24 17:11:51 +11:00
Kayne Ruse f5ebbdf847 link to source 2025-01-22 10:22:22 +11:00
Kayne Ruse 1a3d08958b That overflow-x is odd 2025-01-22 10:14:52 +11:00
Kayne Ruse b1a08289e2 Fixing bounds 2025-01-22 10:07:51 +11:00
Kayne Ruse f1c2e449ea Oh, come on 2025-01-22 09:59:47 +11:00
Kayne Ruse a95ed58cd8 There we go 2025-01-22 09:54:59 +11:00
Kayne Ruse 74a898aa47 Does this work? 2025-01-22 09:49:19 +11:00
Kayne Ruse 32601ae9a9 Hide title on mobile 2025-01-22 09:42:34 +11:00
Kayne Ruse 8610ac2529 Started writing some docs 2025-01-14 13:21:09 +11:00
Kayne Ruse 782f60da9f Is it done? 2025-01-12 16:21:10 +11:00
Kayne Ruse 3a25df60e3 Tweaked text column 2025-01-12 16:19:22 +11:00
Kayne Ruse 7d35c5b325 Trying to fix sort order 2025-01-12 16:16:38 +11:00
Kayne Ruse 1e432387c2 Moved the badge 2025-01-12 16:11:12 +11:00
Kayne Ruse 42dbe038cf Tweaked the style 2025-01-12 16:05:25 +11:00
Kayne Ruse 9563185ef1 filling out placeholder files 2025-01-12 15:17:47 +11:00
Kayne Ruse 1db834f721 Added deploy workflow file 2025-01-12 12:30:42 +11:00
Kayne Ruse f39d725001 Trying a new jekyll theme 2025-01-12 12:26:23 +11:00
Kayne Ruse 98ccfb4215 Forcing a deploy 2025-01-11 22:11:37 +11:00
Kayne Ruse 9d26f94e25 tweak 2025-01-11 22:02:54 +11:00
Kayne Ruse ba4d4fedc1 Attempting to make a deployment mirror 2025-01-11 21:26:22 +11:00
Kayne Ruse b2825690ec Whoops 2025-01-03 16:17:14 +11:00
Kayne Ruse 46d5d7245a I wonder if... 2025-01-03 16:14:15 +11:00
Kayne Ruse b0194db1c4 Fiddling with DNS stuff 2025-01-01 21:36:56 +11:00
Kayne Ruse dc8845fd0e Starting fiddling with the docs site proper 2024-12-27 13:40:49 +11:00
Kayne Ruse 2c4324db70 Update operators.md 2024-11-17 20:20:31 +11:00
Kayne Ruse 28b5c2dab0 Update reserved-words.md 2024-11-17 20:12:13 +11:00
Kayne Ruse ebdc4c6cff Update README.md 2024-11-17 20:09:37 +11:00
Kayne Ruse 151ad7656d Added more reserved words 2024-10-27 18:09:52 +11:00
Kayne Ruse 94fabf2f4e Added types.md 2024-10-27 17:59:44 +11:00
Kayne Ruse 655827f672 Initial commit with a few basic files 2024-10-19 21:11:00 +11:00
118 changed files with 119 additions and 14665 deletions
-19
View File
@@ -1,19 +0,0 @@
name: Comprehensive Tests
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: install valgrind
run: sudo apt install valgrind
- name: make test
run: make test
-31
View File
@@ -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
View File
@@ -0,0 +1 @@
v2.toylang.com
-13
View File
@@ -1,13 +0,0 @@
# License
Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+2 -69
View File
@@ -1,71 +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
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.
This is the Toy programming language interpreter, written in C.
Special thanks to http://craftinginterpreters.com/ for their fantastic book that set me on this path.
# Nifty Features
* Simple C-like syntax
* Bytecode intermediate compilation
* Optional, but robust type system (including `opaque` for arbitrary data)
* Functions and types are first-class citizens
* `import` and `export` variables from the host program
* Fancy slice notation for strings, arrays and dictionaries
* Can re-direct output, error and assertion failure messages
* Open source under the zlib license
## Building
For Windows, Linux and MacOS, simply run `make` in the root directory.
Note: For Linux, you may need to `cd` into the `out` directory before running.
Note: MacOS is not officially supported (no machines for testing), but we'll do our best!
## Tools
Run `make install-tools` to install a number of tools, including:
* VSCode syntax highlighting
## Syntax
```
import standard; //for a bunch of utility functions
print "Hello world"; //"print" is a keyword
var msg = "foobar"; //declare a variable like this
assert true, "This message won't be seen"; //assert is another keyword
//-------------------------
fn makeCounter() { //declare a function like this
var total: int = 0; //declare a variable with a type like this
fn counter(): int { //declare a return type like this
return ++total;
}
return counter; //closures are explicitly supported
}
var tally = makeCounter();
print tally(); //1
print tally(); //2
print tally(); //3
export tally; //export this variable to the host program
```
# License
This source code is covered by the zlib license (see [LICENSE.md](LICENSE.md)).
+4
View File
@@ -0,0 +1,4 @@
title: The Toy Programming Language
description: Documentation For The Toy Programming Language
keywords: programming,coding
author: Kayne Ruse (Ratstail91)
+8
View File
@@ -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>
+21
View File
@@ -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">
+21
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+9
View File
@@ -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>
-51
View File
@@ -1,51 +0,0 @@
DONE: rework type system
DONE: var decl with a type, but no value
DONE: type casting
DONE: remove optimization option
DONE: conditionals
DONE: if-then-else
DONE: chained if-then-else
DONE: optional block around a path if it's only one statement
DONE: while-then
DONE: for-then
DONE: break and continue statements
DONE: truthiness rethink
DONE: string concat with the + operator
DONE: increment & decrement operators
DONE: store compound types in variables
DONE: += -= *= /= %= operators
DONE: && and || operators
DONE: functions are invoked by calling their names
DONE: function arguments can have specified types
DONE: function returns can have specified types
DONE: closures are explicitly supported
DONE: functions are first-class citizens
DONE: functions take a set number of parameters
DONE: functions last argument can be a rest parameter
DONE: assert needs to kill the whole script, not just functions
DONE: native functions
DONE: global functions _get, _set, _push, _pop, _length, clear available
DONE: change comma to colon in dictionary definition
DONE: Address circular references
DONE: are compounds shallow or deep copies? Deep copies
DONE: third output stream, for lexer/parser/compiler/interpreter errors
DONE: Assertion-based test scripts
DONE: Import/export keywords
DONE: A way to check the type of a variable (typeOf keyword)
DONE: slice and dot notation around the builtin _index and _dot functions
DONE: maximum recursion/function depth
DONE: better sugar for _push, _pop, _length
DONE: nested compound assignment bug
DONE: hooks on the external libraries, triggered on import
TODO: standard library
TODO: external script runner library
TODO: document how it all works
TODO: packaging for release?
TODO: test embedding in a game
NOPE: a = b = c = 1;
NOPE: functions return a set number of values
NOPE: ternary operator?
NOPE: Nullish types?
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+18
View File
@@ -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).
-66
View File
@@ -1,66 +0,0 @@
# Optimisation Options
# export CFLAGS+=-O2 -mtune=native -march=native
export TOY_OUTDIR = out
all: $(TOY_OUTDIR) repl
repl: $(TOY_OUTDIR) library
$(MAKE) -C repl
repl-static: $(TOY_OUTDIR) static
$(MAKE) -C repl
library: $(TOY_OUTDIR)
$(MAKE) -j8 -C source library
static: $(TOY_OUTDIR)
$(MAKE) -j8 -C source static
test: clean $(TOY_OUTDIR)
$(MAKE) -C test
$(TOY_OUTDIR):
mkdir $(TOY_OUTDIR)
#utils
install-tools:
cp -rf tools/toylang.vscode-highlighting ~/.vscode/extensions
.PHONY: clean
clean:
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
find . -empty -type d -delete
else ifeq ($(shell uname),Linux)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
rm -rf out
find . -empty -type d -delete
else ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe
else ifeq ($(shell uname),Darwin)
find . -type f -name '*.o' -exec rm -f -r -v {} \;
find . -type f -name '*.a' -exec rm -f -r -v {} \;
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
find . -type f -name '*.dylib' -exec rm -f -r -v {} \;
find . -type f -name '*.so' -exec rm -f -r -v {} \;
rm -rf out
find . -empty -type d -delete
else
@echo "Deletion failed - what platform is this?"
endif
rebuild: clean all
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

-94
View File
@@ -1,94 +0,0 @@
#include "lib_standard.h"
#include "memory.h"
#include <sys/time.h>
static int nativeClock(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to clock\n");
return -1;
}
//get the time from C (what a pain)
time_t rawtime = time(NULL);
struct tm* timeinfo = localtime( &rawtime );
char* timestr = asctime(timeinfo);
//push to the stack
int len = strlen(timestr) - 1; //-1 for the newline
Literal timeLiteral = TO_STRING_LITERAL(copyString(timestr, len), len);
//push to the stack
pushLiteralArray(&interpreter->stack, timeLiteral);
//cleanup
freeLiteral(timeLiteral);
return 1;
}
//call the hook
typedef struct Natives {
char* name;
NativeFn fn;
} Natives;
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias) {
//build the natives list
Natives natives[] = {
{"clock", nativeClock},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
//make sure the name isn't taken
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(alias);
return false;
}
//create the dictionary to load up with functions
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
func.type = LITERAL_FUNCTION_NATIVE;
setLiteralDictionary(dictionary, name, func);
freeLiteral(name);
freeLiteral(func);
}
//build the type
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
TYPE_PUSH_SUBTYPE(&type, strType);
TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
declareScopeVariable(interpreter->scope, alias, type);
setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
freeLiteral(dict);
freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "interpreter.h"
int hookStandard(Interpreter* interpreter, Literal identifier, Literal alias);
-411
View File
@@ -1,411 +0,0 @@
#include "lib_timer.h"
#include "toy_common.h"
#include "memory.h"
#include <stdio.h>
//GOD DAMN IT: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) {
//normallize
if (x->tv_usec > 999999) {
x->tv_sec += x->tv_usec / 1000000;
x->tv_usec %= 1000000;
}
if (y->tv_usec > 999999) {
y->tv_sec += y->tv_usec / 1000000;
y->tv_usec %= 1000000;
}
//calc
result->tv_sec = x->tv_sec - y->tv_sec;
if ((result->tv_usec = x->tv_usec - y->tv_usec) < 0) {
if (result->tv_sec != 0) { //only works far from 0
result->tv_usec += 1000000;
result->tv_sec--; // borrow
}
}
return result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0);
}
//god damn it
static struct timeval* diff(struct timeval* lhs, struct timeval* rhs) {
struct timeval* d = ALLOCATE(struct timeval, 1);
//I gave up, copied from SO
timeval_subtract(d, rhs, lhs);
return d;
}
//callbacks
static int nativeStartTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 0) {
interpreter->errorOutput("Incorrect number of arguments to startTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
gettimeofday(timeinfo, NULL);
//wrap in an opaque literal for Toy
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
pushLiteralArray(&interpreter->stack, timeLiteral);
freeLiteral(timeLiteral);
return 1;
}
static int nativeStopTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _stopTimer\n");
return -1;
}
//get the timeinfo from C
struct timeval timerStop;
gettimeofday(&timerStop, NULL);
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _stopTimer\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timerStart = AS_OPAQUE(timeLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(timerStart, &timerStop);
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(diffLiteral);
return 1;
}
static int nativeCreateTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to createTimer\n");
return -1;
}
//get the args
Literal microsecondLiteral = popLiteralArray(arguments);
Literal secondLiteral = popLiteralArray(arguments);
Literal secondLiteralIdn = secondLiteral;
if (IS_IDENTIFIER(secondLiteral) && parseIdentifierToValue(interpreter, &secondLiteral)) {
freeLiteral(secondLiteralIdn);
}
Literal microsecondLiteralIdn = microsecondLiteral;
if (IS_IDENTIFIER(microsecondLiteral) && parseIdentifierToValue(interpreter, &microsecondLiteral)) {
freeLiteral(microsecondLiteralIdn);
}
if (!IS_INTEGER(secondLiteral) || !IS_INTEGER(microsecondLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to createTimer\n");
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
return -1;
}
if (AS_INTEGER(microsecondLiteral) <= -1000 * 1000 || AS_INTEGER(microsecondLiteral) >= 1000 * 1000 || (AS_INTEGER(secondLiteral) != 0 && AS_INTEGER(microsecondLiteral) < 0) ) {
interpreter->errorOutput("Microseconds out of range in createTimer\n");
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
return -1;
}
//get the timeinfo from toy
struct timeval* timeinfo = ALLOCATE(struct timeval, 1);
timeinfo->tv_sec = AS_INTEGER(secondLiteral);
timeinfo->tv_usec = AS_INTEGER(microsecondLiteral);
//wrap in an opaque literal for Toy
Literal timeLiteral = TO_OPAQUE_LITERAL(timeinfo, -1);
pushLiteralArray(&interpreter->stack, timeLiteral);
freeLiteral(timeLiteral);
freeLiteral(secondLiteral);
freeLiteral(microsecondLiteral);
return 1;
}
static int nativeGetTimerSeconds(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerSeconds\n");
return -1;
}
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerSeconds\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
//create the result literal
Literal result = TO_INTEGER_LITERAL(timer->tv_sec);
pushLiteralArray(&interpreter->stack, result);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(result);
return 1;
}
static int nativeGetTimerMicroseconds(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _getTimerMicroseconds\n");
return -1;
}
//unwrap the opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _getTimerMicroseconds\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
//create the result literal
Literal result = TO_INTEGER_LITERAL(timer->tv_usec);
pushLiteralArray(&interpreter->stack, result);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(result);
return 1;
}
static int nativeCompareTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 2) {
interpreter->errorOutput("Incorrect number of arguments to _compareTimer\n");
return -1;
}
//unwrap the opaque literals
Literal rhsLiteral = popLiteralArray(arguments);
Literal lhsLiteral = popLiteralArray(arguments);
Literal lhsLiteralIdn = lhsLiteral;
if (IS_IDENTIFIER(lhsLiteral) && parseIdentifierToValue(interpreter, &lhsLiteral)) {
freeLiteral(lhsLiteralIdn);
}
Literal rhsLiteralIdn = rhsLiteral;
if (IS_IDENTIFIER(rhsLiteral) && parseIdentifierToValue(interpreter, &rhsLiteral)) {
freeLiteral(rhsLiteralIdn);
}
if (!IS_OPAQUE(lhsLiteral) || !IS_OPAQUE(rhsLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _compareTimer\n");
freeLiteral(lhsLiteral);
freeLiteral(rhsLiteral);
return -1;
}
struct timeval* lhsTimer = AS_OPAQUE(lhsLiteral);
struct timeval* rhsTimer = AS_OPAQUE(rhsLiteral);
//determine the difference, and wrap it
struct timeval* d = diff(lhsTimer, rhsTimer);
Literal diffLiteral = TO_OPAQUE_LITERAL(d, -1);
pushLiteralArray(&interpreter->stack, diffLiteral);
//cleanup
freeLiteral(lhsLiteral);
freeLiteral(rhsLiteral);
freeLiteral(diffLiteral);
return 1;
}
static int nativeTimerToString(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _timerToString\n");
return -1;
}
//unwrap in an opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _timerToString\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
//create the string literal
Literal resultLiteral = TO_NULL_LITERAL;
if (timer->tv_sec == 0 && timer->tv_usec < 0) { //special case, for when the negative sign is encoded in the usec
char buffer[128];
snprintf(buffer, 128, "-%ld.%06ld", timer->tv_sec, -timer->tv_usec);
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
}
else { //normal case
char buffer[128];
snprintf(buffer, 128, "%ld.%06ld", timer->tv_sec, timer->tv_usec);
resultLiteral = TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer));
}
pushLiteralArray(&interpreter->stack, resultLiteral);
//cleanup
freeLiteral(timeLiteral);
freeLiteral(resultLiteral);
return 1;
}
static int nativeDestroyTimer(Interpreter* interpreter, LiteralArray* arguments) {
//no arguments
if (arguments->count != 1) {
interpreter->errorOutput("Incorrect number of arguments to _destroyTimer\n");
return -1;
}
//unwrap in an opaque literal
Literal timeLiteral = popLiteralArray(arguments);
Literal timeLiteralIdn = timeLiteral;
if (IS_IDENTIFIER(timeLiteral) && parseIdentifierToValue(interpreter, &timeLiteral)) {
freeLiteral(timeLiteralIdn);
}
if (!IS_OPAQUE(timeLiteral)) {
interpreter->errorOutput("Incorrect argument type passed to _destroyTimer\n");
freeLiteral(timeLiteral);
return -1;
}
struct timeval* timer = AS_OPAQUE(timeLiteral);
FREE(struct timeval, timer);
freeLiteral(timeLiteral);
return 0;
}
//call the hook
typedef struct Natives {
char* name;
NativeFn fn;
} Natives;
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias) {
//build the natives list
Natives natives[] = {
{"startTimer", nativeStartTimer},
{"_stopTimer", nativeStopTimer},
{"createTimer", nativeCreateTimer},
{"_getTimerSeconds", nativeGetTimerSeconds},
{"_getTimerMicroseconds", nativeGetTimerMicroseconds},
{"_compareTimer", nativeCompareTimer},
{"_timerToString", nativeTimerToString},
{"_destroyTimer", nativeDestroyTimer},
{NULL, NULL}
};
//store the library in an aliased dictionary
if (!IS_NULL(alias)) {
//make sure the name isn't taken
if (isDelcaredScopeVariable(interpreter->scope, alias)) {
interpreter->errorOutput("Can't override an existing variable\n");
freeLiteral(alias);
return false;
}
//create the dictionary to load up with functions
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
//load the dict with functions
for (int i = 0; natives[i].name; i++) {
Literal name = TO_STRING_LITERAL(copyString(natives[i].name, strlen(natives[i].name)), strlen(natives[i].name));
Literal func = TO_FUNCTION_LITERAL((void*)natives[i].fn, 0);
func.type = LITERAL_FUNCTION_NATIVE;
setLiteralDictionary(dictionary, name, func);
freeLiteral(name);
freeLiteral(func);
}
//build the type
Literal type = TO_TYPE_LITERAL(LITERAL_DICTIONARY, true);
Literal strType = TO_TYPE_LITERAL(LITERAL_STRING, true);
Literal fnType = TO_TYPE_LITERAL(LITERAL_FUNCTION_NATIVE, true);
TYPE_PUSH_SUBTYPE(&type, strType);
TYPE_PUSH_SUBTYPE(&type, fnType);
//set scope
Literal dict = TO_DICTIONARY_LITERAL(dictionary);
declareScopeVariable(interpreter->scope, alias, type);
setScopeVariable(interpreter->scope, alias, dict, false);
//cleanup
freeLiteral(dict);
freeLiteral(type);
return 0;
}
//default
for (int i = 0; natives[i].name; i++) {
injectNativeFn(interpreter, natives[i].name, natives[i].fn);
}
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include "interpreter.h"
int hookTimer(Interpreter* interpreter, Literal identifier, Literal alias);
-33
View File
@@ -1,33 +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
$(OBJ): | $(ODIR)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) $(ODIR)
rm /usr/local/lib/lib$(OUTNAME).dylib
-141
View File
@@ -1,141 +0,0 @@
#include "repl_tools.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "console_colors.h"
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void repl() {
//repl does it's own thing for now
bool error = false;
const int size = 2048;
char input[size];
memset(input, 0, size);
Interpreter interpreter; //persist the interpreter for the scopes
initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
for(;;) {
printf("> ");
fgets(input, size, stdin);
//escape the repl (length of 5 to accomodate the newline)
if (strlen(input) == 5 && (!strncmp(input, "exit", 4) || !strncmp(input, "quit", 4))) {
break;
}
//setup this iteration
Lexer lexer;
Parser parser;
Compiler compiler;
initLexer(&lexer, input);
initParser(&parser, &lexer);
initCompiler(&compiler);
//run this iteration
ASTNode* node = scanParser(&parser);
while(node != NULL) {
//pack up and restart
if (node->type == AST_NODEERROR) {
printf(ERROR "error node detected\n" RESET);
error = true;
freeNode(node);
break;
}
writeCompiler(&compiler, node);
freeNode(node);
node = scanParser(&parser);
}
if (!error) {
//get the bytecode dump
int size = 0;
unsigned char* tb = collateCompiler(&compiler, &size);
//run the bytecode
runInterpreter(&interpreter, tb, size);
}
//clean up this iteration
freeCompiler(&compiler);
freeParser(&parser);
error = false;
}
freeInterpreter(&interpreter);
}
//entry point
int main(int argc, const char* argv[]) {
initCommand(argc, argv);
//command specific actions
if (command.error) {
usageCommand(argc, argv);
return 0;
}
if (command.help) {
helpCommand(argc, argv);
return 0;
}
if (command.version) {
copyrightCommand(argc, argv);
return 0;
}
//TODO: remove this when the interpreter meets the specification
if (command.verbose) {
printf(NOTICE "Toy Programming Language Version %d.%d.%d\n" RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
}
//run source file
if (command.sourcefile) {
runSourceFile(command.sourcefile);
return 0;
}
//run from stdin
if (command.source) {
runSource(command.source);
return 0;
}
//compile source file
if (command.compilefile && command.outfile) {
size_t size = 0;
char* source = readFile(command.compilefile, &size);
unsigned char* tb = compileString(source, &size);
if (!tb) {
return 1;
}
writeFile(command.outfile, tb, size);
return 0;
}
//run binary
if (command.binaryfile) {
runBinaryFile(command.binaryfile);
return 0;
}
repl();
return 0;
}
-143
View File
@@ -1,143 +0,0 @@
#include "repl_tools.h"
#include "lib_standard.h"
#include "lib_timer.h"
#include "console_colors.h"
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include <stdio.h>
#include <stdlib.h>
//IO functions
char* readFile(char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
if (file == NULL) {
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
exit(-1);
}
fseek(file, 0L, SEEK_END);
*fileSize = ftell(file);
rewind(file);
char* buffer = (char*)malloc(*fileSize + 1);
if (buffer == NULL) {
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
exit(-1);
}
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
if (bytesRead < *fileSize) {
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
exit(-1);
}
fclose(file);
return buffer;
}
void writeFile(char* path, unsigned char* bytes, size_t size) {
FILE* file = fopen(path, "wb");
if (file == NULL) {
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
exit(-1);
}
int written = fwrite(bytes, size, 1, file);
if (written != 1) {
fprintf(stderr, ERROR "Could not write file \"%s\"\n" RESET, path);
exit(-1);
}
fclose(file);
}
//repl functions
unsigned char* compileString(char* source, size_t* size) {
Lexer lexer;
Parser parser;
Compiler compiler;
initLexer(&lexer, source);
initParser(&parser, &lexer);
initCompiler(&compiler);
//run the parser until the end of the source
ASTNode* node = scanParser(&parser);
while(node != NULL) {
//pack up and leave
if (node->type == AST_NODEERROR) {
printf(ERROR "error node detected\n" RESET);
freeNode(node);
freeCompiler(&compiler);
freeParser(&parser);
return NULL;
}
writeCompiler(&compiler, node);
freeNode(node);
node = scanParser(&parser);
}
//get the bytecode dump
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
//cleanup
freeCompiler(&compiler);
freeParser(&parser);
//no lexer to clean up
//finally
return tb;
}
void runBinary(unsigned char* tb, size_t size) {
Interpreter interpreter;
initInterpreter(&interpreter);
//inject the libs
injectNativeHook(&interpreter, "standard", hookStandard);
injectNativeHook(&interpreter, "timer", hookTimer);
runInterpreter(&interpreter, tb, size);
freeInterpreter(&interpreter);
}
void runBinaryFile(char* fname) {
size_t size = 0; //not used
unsigned char* tb = (unsigned char*)readFile(fname, &size);
if (!tb) {
return;
}
runBinary(tb, size);
//interpreter takes ownership of the binary data
}
void runSource(char* source) {
size_t size = 0;
unsigned char* tb = compileString(source, &size);
if (!tb) {
return;
}
runBinary(tb, size);
}
void runSourceFile(char* fname) {
size_t size = 0; //not used
char* source = readFile(fname, &size);
runSource(source);
free((void*)source);
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "toy_common.h"
char* readFile(char* path, size_t* fileSize);
void writeFile(char* path, unsigned char* bytes, size_t size);
unsigned char* compileString(char* source, size_t* size);
void runBinary(unsigned char* tb, size_t size);
void runBinaryFile(char* fname);
void runSource(char* source);
void runSourceFile(char* fname);
+16
View File
@@ -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

-89
View File
@@ -1,89 +0,0 @@
//single line comment
/*
multi line comment
*/
//test primitive literals
print "hello world";
print null;
print true;
print false;
print 42;
print 3.14;
print -69;
print -4.20;
print 2 + (3 * 3);
//test operators (integers)
print 1 + 1;
print 1 - 1;
print 2 * 2;
print 1 / 2;
print 4 % 2;
//test operators (floats)
print 1.0 + 1.0;
print 1.0 - 1.0;
print 2.0 * 2.0;
print 1.0 / 2.0;
//test scopes
{
print "This statement is within a scope.";
{
print "This is a deeper scope.";
}
}
print "Back to the outer scope.";
//test scope will delegate to higher scope
var a = 1;
{
a = 2;
print a;
}
print a;
//test scope will shadow higher scope on redefine
var b: int = 3;
{
var b = 4;
print b;
}
print b;
//test compounds, repeatedly
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
print [1, 2, 3];
print [4, 5];
print ["key":"value"];
//test empties
print [];
print [:];
//test nested compounds
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
//var declarations
var x = 31;
var y : int = 42;
var arr : [int] = [1, 2, 3, 42];
var dict : [string:int] = ["hello": 1, "world":2];
//printing expressions
print x;
print x + y;
print arr;
print dict;
//test asserts at the end of the file
assert x, "This won't be seen";
assert true, "This won't be seen";
assert false, "This is a failed assert, and will end execution";
print "This will not be printed because of the above assert";
-12
View File
@@ -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;
}
-66
View File
@@ -1,66 +0,0 @@
var size: int const = 100;
var prev = [];
for (var i = 0; i < size; i++) {
prev.push(false);
}
prev.set(size - 1, true);
fn calc(p, i) {
if (p[i-1] && p[i] && p[i+1]) {
return false;
}
if (p[i-1] && p[i] && !p[i+1]) {
return true;
}
if (p[i-1] && !p[i] && p[i+1]) {
return true;
}
if (p[i-1] && !p[i] && !p[i+1]) {
return false;
}
if (!p[i-1] && p[i] && p[i+1]) {
return true;
}
if (!p[i-1] && p[i] && !p[i+1]) {
return true;
}
if (!p[i-1] && !p[i] && p[i+1]) {
return true;
}
if (!p[i-1] && !p[i] && !p[i+1]) {
return false;
}
}
for (var iteration = 0; iteration < 100; iteration++) {
var line = [false];
for (var i = 1; i < size-1; i++) {
line.push(calc(prev, i));
}
line.push(false);
var output = "";
for (var i = 0; i < line.length(); i++) {
if (line[i]) {
output += "*";
}
else {
output += " ";
}
}
print output;
prev = line;
}
-13
View File
@@ -1,13 +0,0 @@
import timer;
var a = createTimer(1, 0);
var b = createTimer(2, 0);
print a.compareTimer(b).timerToString();
print b.compareTimer(a).timerToString();
var c = createTimer(0, 1);
var d = createTimer(0, 2);
print c.compareTimer(d).timerToString();
print d.compareTimer(c).timerToString();
+19
View File
@@ -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"
}
-294
View File
@@ -1,294 +0,0 @@
#include "ast_node.h"
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>
void freeNodeCustom(ASTNode* node, bool freeSelf) {
//don't free a NULL node
if (node == NULL) {
return;
}
switch(node->type) {
case AST_NODEERROR:
//NO-OP
break;
case AST_NODELITERAL:
freeLiteral(node->atomic.literal);
break;
case AST_NODEUNARY:
freeNode(node->unary.child);
break;
case AST_NODEBINARY:
freeNode(node->binary.left);
freeNode(node->binary.right);
break;
case AST_NODEGROUPING:
freeNode(node->grouping.child);
break;
case AST_NODEBLOCK:
for (int i = 0; i < node->block.count; i++) {
freeNodeCustom(node->block.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->block.nodes, node->block.capacity);
break;
case AST_NODECOMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeNodeCustom(node->compound.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->compound.nodes, node->compound.capacity);
break;
case AST_NODEPAIR:
freeNode(node->pair.left);
freeNode(node->pair.right);
break;
case AST_NODEVAR_DECL:
freeLiteral(node->varDecl.identifier);
freeLiteral(node->varDecl.typeLiteral);
freeNode(node->varDecl.expression);
break;
case AST_NODEFN_DECL:
freeLiteral(node->fnDecl.identifier);
freeNode(node->fnDecl.arguments);
freeNode(node->fnDecl.returns);
freeNode(node->fnDecl.block);
break;
case AST_NODEFN_COLLECTION:
for (int i = 0; i < node->fnCollection.count; i++) {
freeNodeCustom(node->fnCollection.nodes + i, false);
}
FREE_ARRAY(ASTNode, node->fnCollection.nodes, node->fnCollection.capacity);
break;
case AST_NODEFN_CALL:
freeNode(node->fnCall.arguments);
break;
case AST_NODEPATH_IF:
case AST_NODEPATH_WHILE:
case AST_NODEPATH_FOR:
case AST_NODEPATH_BREAK:
case AST_NODEPATH_CONTINUE:
case AST_NODEPATH_RETURN:
freeNode(node->path.preClause);
freeNode(node->path.postClause);
freeNode(node->path.condition);
freeNode(node->path.thenPath);
freeNode(node->path.elsePath);
break;
case AST_NODEINCREMENT_PREFIX:
case AST_NODEINCREMENT_POSTFIX:
freeLiteral(node->increment.identifier);
break;
case AST_NODEIMPORT:
case AST_NODEEXPORT:
freeLiteral(node->import.identifier);
freeLiteral(node->import.alias);
break;
case AST_NODEINDEX:
case AST_NODEDOT:
freeNode(node->index.first);
freeNode(node->index.second);
freeNode(node->index.third);
break;
}
if (freeSelf) {
FREE(ASTNode, node);
}
}
void freeNode(ASTNode* node) {
freeNodeCustom(node, true);
}
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODELITERAL;
(*nodeHandle)->atomic.literal = copyLiteral(literal);
}
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child) {
//allocate a new node
*nodeHandle = ALLOCATE(ASTNode, 1);
(*nodeHandle)->type = AST_NODEUNARY;
(*nodeHandle)->unary.opcode = opcode;
(*nodeHandle)->unary.child = child;
}
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEBINARY;
tmp->binary.opcode = opcode;
tmp->binary.left = *nodeHandle;
tmp->binary.right = rhs;
*nodeHandle = tmp;
}
void emitASTNodeGrouping(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEGROUPING;
tmp->grouping.child = *nodeHandle;
*nodeHandle = tmp;
}
void emitASTNodeBlock(ASTNode** nodeHandle) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEBLOCK;
tmp->block.nodes = NULL;
tmp->block.capacity = 0;
tmp->block.count = 0;
*nodeHandle = tmp;
}
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODECOMPOUND;
tmp->compound.literalType = literalType;
tmp->compound.nodes = NULL;
tmp->compound.capacity = 0;
tmp->compound.count = 0;
*nodeHandle = tmp;
}
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right) {
//assume the node has already been allocated
node->type = AST_NODEPAIR;
node->pair.left = left;
node->pair.right = right;
}
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal typeLiteral, ASTNode* expression) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEVAR_DECL;
tmp->varDecl.identifier = identifier;
tmp->varDecl.typeLiteral = typeLiteral;
tmp->varDecl.expression = expression;
*nodeHandle = tmp;
}
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_DECL;
tmp->fnDecl.identifier = identifier;
tmp->fnDecl.arguments = arguments;
tmp->fnDecl.returns = returns;
tmp->fnDecl.block = block;
*nodeHandle = tmp;
}
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_CALL;
tmp->fnCall.arguments = arguments;
tmp->fnCall.argumentCount = argumentCount;
*nodeHandle = tmp;
}
void emitASTNodeFnCollection(ASTNode** nodeHandle) { //a collection of nodes, intended for use with functions
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEFN_COLLECTION;
tmp->fnCollection.nodes = NULL;
tmp->fnCollection.capacity = 0;
tmp->fnCollection.count = 0;
*nodeHandle = tmp;
}
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = type;
tmp->path.preClause = preClause;
tmp->path.postClause = postClause;
tmp->path.condition = condition;
tmp->path.thenPath = thenPath;
tmp->path.elsePath = elsePath;
*nodeHandle = tmp;
}
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINCREMENT_PREFIX;
tmp->increment.identifier = copyLiteral(identifier);
tmp->increment.increment = increment;
*nodeHandle = tmp;
}
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINCREMENT_POSTFIX;
tmp->increment.identifier = copyLiteral(identifier);
tmp->increment.increment = increment;
*nodeHandle = tmp;
}
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = mode;
tmp->import.identifier = copyLiteral(identifier);
tmp->import.alias = copyLiteral(alias);
*nodeHandle = tmp;
}
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEINDEX;
tmp->index.first = first;
tmp->index.second = second;
tmp->index.third = third;
*nodeHandle = tmp;
}
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first) {
ASTNode* tmp = ALLOCATE(ASTNode, 1);
tmp->type = AST_NODEDOT;
tmp->index.first = first;
tmp->index.second = NULL;
tmp->index.third = NULL;
*nodeHandle = tmp;
}
-175
View File
@@ -1,175 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
#include "opcodes.h"
#include "token_types.h"
//nodes are the intermediaries between parsers and compilers
typedef union _node ASTNode;
typedef enum ASTNodeType {
AST_NODEERROR,
AST_NODELITERAL, //a simple value
AST_NODEUNARY, //one child + opcode
AST_NODEBINARY, //two children, left and right + opcode
AST_NODEGROUPING, //one child
AST_NODEBLOCK, //contains a sub-node array
AST_NODECOMPOUND, //contains a sub-node array
AST_NODEPAIR, //contains a left and right
AST_NODEVAR_DECL, //contains identifier literal, typenode, expression definition
AST_NODEFN_DECL, //containd identifier literal, arguments node, returns node, block node
AST_NODEFN_COLLECTION, //parts of a function
AST_NODEFN_CALL,
AST_NODEPATH_IF, //for control flow
AST_NODEPATH_WHILE, //for control flow
AST_NODEPATH_FOR, //for control flow
AST_NODEPATH_BREAK, //for control flow
AST_NODEPATH_CONTINUE, //for control flow
AST_NODEPATH_RETURN,
AST_NODEINCREMENT_PREFIX,
AST_NODEINCREMENT_POSTFIX,
AST_NODEIMPORT,
AST_NODEEXPORT,
AST_NODEINDEX,
AST_NODEDOT,
} ASTNodeType;
typedef struct NodeLiteral {
ASTNodeType type;
Literal literal;
} NodeLiteral;
typedef struct NodeUnary {
ASTNodeType type;
Opcode opcode;
ASTNode* child;
} NodeUnary;
typedef struct NodeBinary {
ASTNodeType type;
Opcode opcode;
ASTNode* left;
ASTNode* right;
} NodeBinary;
typedef struct NodeGrouping {
ASTNodeType type;
ASTNode* child;
} NodeGrouping;
typedef struct NodeBlock {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeBlock;
typedef struct NodeCompound {
ASTNodeType type;
LiteralType literalType;
ASTNode* nodes;
int capacity;
int count;
} NodeCompound;
typedef struct NodePair {
ASTNodeType type;
ASTNode* left;
ASTNode* right;
} NodePair;
typedef struct NodeVarDecl {
ASTNodeType type;
Literal identifier;
Literal typeLiteral;
ASTNode* expression;
} NodeVarDecl;
typedef struct NodeFnDecl {
ASTNodeType type;
Literal identifier;
ASTNode* arguments;
ASTNode* returns;
ASTNode* block;
} NodeFnDecl;
typedef struct NodeFnCollection {
ASTNodeType type;
ASTNode* nodes;
int capacity;
int count;
} NodeFnCollection;
typedef struct NodeFnCall {
ASTNodeType type;
ASTNode* arguments;
int argumentCount;
} NodeFnCall;
typedef struct NodePath {
ASTNodeType type;
ASTNode* preClause;
ASTNode* postClause;
ASTNode* condition;
ASTNode* thenPath;
ASTNode* elsePath;
} NodePath;
typedef struct NodeIncrement {
ASTNodeType type;
Literal identifier;
int increment;
} NodeIncrement;
typedef struct NodeImport {
ASTNodeType type;
Literal identifier;
Literal alias;
} NodeImport;
typedef struct NodeIndex {
ASTNodeType type;
ASTNode* first;
ASTNode* second;
ASTNode* third;
} NodeIndex;
union _node {
ASTNodeType type;
NodeLiteral atomic;
NodeUnary unary;
NodeBinary binary;
NodeGrouping grouping;
NodeBlock block;
NodeCompound compound;
NodePair pair;
NodeVarDecl varDecl;
NodeFnDecl fnDecl;
NodeFnCollection fnCollection;
NodeFnCall fnCall;
NodePath path;
NodeIncrement increment;
NodeImport import;
NodeIndex index;
};
TOY_API void freeNode(ASTNode* node);
void emitASTNodeLiteral(ASTNode** nodeHandle, Literal literal);
void emitASTNodeUnary(ASTNode** nodeHandle, Opcode opcode, ASTNode* child);
void emitASTNodeBinary(ASTNode** nodeHandle, ASTNode* rhs, Opcode opcode); //handled node becomes lhs
void emitASTNodeGrouping(ASTNode** nodeHandle);
void emitASTNodeBlock(ASTNode** nodeHandle);
void emitASTNodeCompound(ASTNode** nodeHandle, LiteralType literalType);
void setASTNodePair(ASTNode* node, ASTNode* left, ASTNode* right);
void emitASTNodeVarDecl(ASTNode** nodeHandle, Literal identifier, Literal type, ASTNode* expression);
void emitASTNodeFnDecl(ASTNode** nodeHandle, Literal identifier, ASTNode* arguments, ASTNode* returns, ASTNode* block);
void emitASTFnCall(ASTNode** nodeHandle, ASTNode* arguments, int argumentCount);
void emitASTNodeFnCollection(ASTNode** nodeHandle);
void emitASTNodePath(ASTNode** nodeHandle, ASTNodeType type, ASTNode* preClause, ASTNode* postClause, ASTNode* condition, ASTNode* thenPath, ASTNode* elsePath);
void emitASTNodePrefixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
void emitASTNodePostfixIncrement(ASTNode** nodeHandle, Literal identifier, int increment);
void emitASTNodeImport(ASTNode** nodeHandle, ASTNodeType mode, Literal identifier, Literal alias);
void emitASTNodeIndex(ASTNode** nodeHandle, ASTNode* first, ASTNode* second, ASTNode* third);
void emitASTNodeDot(ASTNode** nodeHandle, ASTNode* first);
-1345
View File
File diff suppressed because it is too large Load Diff
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#include "interpreter.h"
int _index(Interpreter* interpreter, LiteralArray* arguments);
int _set(Interpreter* interpreter, LiteralArray* arguments);
int _get(Interpreter* interpreter, LiteralArray* arguments);
int _push(Interpreter* interpreter, LiteralArray* arguments);
int _pop(Interpreter* interpreter, LiteralArray* arguments);
int _length(Interpreter* interpreter, LiteralArray* arguments);
int _clear(Interpreter* interpreter, LiteralArray* arguments);
-1164
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include "toy_common.h"
#include "opcodes.h"
#include "ast_node.h"
#include "literal_array.h"
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
typedef struct Compiler {
LiteralArray literalCache;
unsigned char* bytecode;
int capacity;
int count;
} Compiler;
TOY_API void initCompiler(Compiler* compiler);
TOY_API void writeCompiler(Compiler* compiler, ASTNode* node);
TOY_API void freeCompiler(Compiler* compiler);
//embed the header, data section, code section, function section, etc.
TOY_API unsigned char* collateCompiler(Compiler* compiler, int* size);
-30
View File
@@ -1,30 +0,0 @@
#pragma once
//NOTE: you need both font AND background for these to work
//fonts color
#define FONT_BLACK "\033[30;"
#define FONT_RED "\033[31;"
#define FONT_GREEN "\033[32;"
#define FONT_YELLOW "\033[33;"
#define FONT_BLUE "\033[34;"
#define FONT_PURPLE "\033[35;"
#define FONT_DGREEN "\033[6;"
#define FONT_WHITE "\033[7;"
#define FONT_CYAN "\x1b[36m"
//background color
#define BACK_BLACK "40m"
#define BACK_RED "41m"
#define BACK_GREEN "42m"
#define BACK_YELLOW "43m"
#define BACK_BLUE "44m"
#define BACK_PURPLE "45m"
#define BACK_DGREEN "46m"
#define BACK_WHITE "47m"
//useful
#define NOTICE FONT_GREEN BACK_BLACK
#define WARN FONT_YELLOW BACK_BLACK
#define ERROR FONT_RED BACK_BLACK
#define RESET "\033[0m"
-2543
View File
File diff suppressed because it is too large Load Diff
-57
View File
@@ -1,57 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
typedef void (*PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions
typedef struct Interpreter {
//input
unsigned char* bytecode;
int length;
int count;
int codeStart; //BUGFIX: for jumps, must be initialized to -1
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
//operation
Scope* scope;
LiteralArray stack;
LiteralDictionary* exports; //read-write - interface with Toy from C - this is a pointer, since it works at a script-level
LiteralDictionary* exportTypes;
LiteralDictionary* hooks;
//debug outputs
PrintFn printOutput;
PrintFn assertOutput;
PrintFn errorOutput;
int depth; //don't overflow
bool panic;
} Interpreter;
//native API
typedef int (*NativeFn)(Interpreter* interpreter, LiteralArray* arguments);
TOY_API bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func);
typedef int (*HookFn)(Interpreter* interpreter, Literal identifier, Literal alias);
TOY_API bool injectNativeHook(Interpreter* interpreter, char* name, HookFn hook);
TOY_API bool callLiteralFn(Interpreter* interpreter, Literal func, LiteralArray* arguments, LiteralArray* returns);
TOY_API bool callFn(Interpreter* interpreter, char* name, LiteralArray* arguments, LiteralArray* returns);
//utilities for the host program
TOY_API bool parseIdentifierToValue(Interpreter* interpreter, Literal* literalPtr);
TOY_API void setInterpreterPrint(Interpreter* interpreter, PrintFn printOutput);
TOY_API void setInterpreterAssert(Interpreter* interpreter, PrintFn assertOutput);
TOY_API void setInterpreterError(Interpreter* interpreter, PrintFn errorOutput);
//main access
TOY_API void initInterpreter(Interpreter* interpreter); //start of program
TOY_API void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length); //run the code
TOY_API void resetInterpreter(Interpreter* interpreter); //use this to reset the interpreter's environment between runs
TOY_API void freeInterpreter(Interpreter* interpreter); //end of program
-77
View File
@@ -1,77 +0,0 @@
#include "keyword_types.h"
#include "toy_common.h"
#include <string.h>
KeywordType keywordTypes[] = {
//type keywords
{TOKEN_NULL, "null"},
{TOKEN_BOOLEAN, "bool"},
{TOKEN_INTEGER, "int"},
{TOKEN_FLOAT, "float"},
{TOKEN_STRING, "string"},
{TOKEN_FUNCTION, "fn"},
{TOKEN_OPAQUE, "opaque"},
{TOKEN_ANY, "any"},
//other keywords
{TOKEN_AS, "as"},
{TOKEN_ASSERT, "assert"},
{TOKEN_BREAK, "break"},
{TOKEN_CLASS, "class"},
{TOKEN_CONST, "const"},
{TOKEN_CONTINUE, "continue"},
{TOKEN_DO, "do"},
{TOKEN_ELSE, "else"},
{TOKEN_EXPORT, "export"},
{TOKEN_FOR, "for"},
{TOKEN_FOREACH, "foreach"},
{TOKEN_IF, "if"},
{TOKEN_IMPORT, "import"},
{TOKEN_IN, "in"},
{TOKEN_OF, "of"},
{TOKEN_PRINT, "print"},
{TOKEN_RETURN, "return"},
{TOKEN_TYPE, "type"},
{TOKEN_ASTYPE, "astype"},
{TOKEN_TYPEOF, "typeof"},
{TOKEN_VAR, "var"},
{TOKEN_WHILE, "while"},
//literal values
{TOKEN_LITERAL_TRUE, "true"},
{TOKEN_LITERAL_FALSE, "false"},
//meta tokens
{TOKEN_PASS, NULL},
{TOKEN_ERROR, NULL},
{TOKEN_EOF, NULL},
};
char* findKeywordByType(TokenType type) {
if (type == TOKEN_EOF) {
return "EOF";
}
for(int i = 0; keywordTypes[i].keyword; i++) {
if (keywordTypes[i].type == type) {
return keywordTypes[i].keyword;
}
}
return NULL;
}
TokenType findTypeByKeyword(const char* keyword) {
const int length = strlen(keyword);
for (int i = 0; keywordTypes[i].keyword; i++) {
if (!strncmp(keyword, keywordTypes[i].keyword, length)) {
return keywordTypes[i].type;
}
}
return TOKEN_EOF;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "token_types.h"
typedef struct {
TokenType type;
char* keyword;
} KeywordType;
extern KeywordType keywordTypes[];
char* findKeywordByType(TokenType type);
TokenType findTypeByKeyword(const char* keyword);
-348
View File
@@ -1,348 +0,0 @@
#include "lexer.h"
#include "console_colors.h"
#include "keyword_types.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//static generic utility functions
static void cleanLexer(Lexer* lexer) {
lexer->source = NULL;
lexer->start = 0;
lexer->current = 0;
lexer->line = 1;
}
static bool isAtEnd(Lexer* lexer) {
return lexer->source[lexer->current] == '\0';
}
static char peek(Lexer* lexer) {
return lexer->source[lexer->current];
}
static char peekNext(Lexer* lexer) {
if (isAtEnd(lexer)) return '\0';
return lexer->source[lexer->current + 1];
}
static char advance(Lexer* lexer) {
if (isAtEnd(lexer)) {
return '\0';
}
//new line
if (lexer->source[lexer->current] == '\n') {
lexer->line++;
}
lexer->current++;
return lexer->source[lexer->current - 1];
}
static void eatWhitespace(Lexer* lexer) {
const char c = peek(lexer);
switch(c) {
case ' ':
case '\r':
case '\n':
case '\t':
advance(lexer);
break;
//comments
case '/':
//eat the line
if (peekNext(lexer) == '/') {
while (advance(lexer) != '\n' && !isAtEnd(lexer));
break;
}
//eat the block
if (peekNext(lexer) == '*') {
advance(lexer);
advance(lexer);
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
advance(lexer);
advance(lexer);
break;
}
return;
default:
return;
}
//tail recursion
eatWhitespace(lexer);
}
static bool isDigit(Lexer* lexer) {
return peek(lexer) >= '0' && peek(lexer) <= '9';
}
static bool isAlpha(Lexer* lexer) {
return
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
peek(lexer) == '_'
;
}
static bool match(Lexer* lexer, char c) {
if (peek(lexer) == c) {
advance(lexer);
return true;
}
return false;
}
//token generators
static Token makeErrorToken(Lexer* lexer, char* msg) {
Token token;
token.type = TOKEN_ERROR;
token.lexeme = msg;
token.length = strlen(msg);
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("err:");
printToken(&token);
}
#endif
return token;
}
static Token makeToken(Lexer* lexer, TokenType type) {
Token token;
token.type = type;
token.length = lexer->current - lexer->start;
token.lexeme = &lexer->source[lexer->current - token.length];
token.line = lexer->line;
#ifndef TOY_EXPORT
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed
if (command.verbose) {
printf("tok:");
printToken(&token);
}
#endif
return token;
}
static Token makeIntegerOrFloat(Lexer* lexer) {
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
while(isDigit(lexer)) advance(lexer);
if (peek(lexer) == '.' && (peekNext(lexer) >= '0' && peekNext(lexer) <= '9')) { //BUGFIX: peekNext == digit
type = TOKEN_LITERAL_FLOAT;
advance(lexer);
while(isDigit(lexer)) advance(lexer);
}
Token token;
token.type = type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
if (type == TOKEN_LITERAL_INTEGER) {
printf("int:");
} else {
printf("flt:");
}
printToken(&token);
}
#endif
return token;
}
static Token makeString(Lexer* lexer, char terminator) {
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
advance(lexer);
}
advance(lexer); //eat terminator
if (isAtEnd(lexer)) {
return makeErrorToken(lexer, "Unterminated string");
}
Token token;
token.type = TOKEN_LITERAL_STRING;
token.lexeme = &lexer->source[lexer->start + 1];
token.length = lexer->current - lexer->start - 2;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("str:");
printToken(&token);
}
#endif
return token;
}
static Token makeKeywordOrIdentifier(Lexer* lexer) {
advance(lexer); //first letter can only be alpha
while(isDigit(lexer) || isAlpha(lexer)) {
advance(lexer);
}
//scan for a keyword
for (int i = 0; keywordTypes[i].keyword; i++) {
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
Token token;
token.type = keywordTypes[i].type;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("kwd:");
printToken(&token);
}
#endif
return token;
}
}
//return an identifier
Token token;
token.type = TOKEN_IDENTIFIER;
token.lexeme = &lexer->source[lexer->start];
token.length = lexer->current - lexer->start;
token.line = lexer->line;
#ifndef TOY_EXPORT
if (command.verbose) {
printf("idf:");
printToken(&token);
}
#endif
return token;
}
//exposed functions
void initLexer(Lexer* lexer, char* source) {
cleanLexer(lexer);
lexer->source = source;
}
Token scanLexer(Lexer* lexer) {
eatWhitespace(lexer);
lexer->start = lexer->current;
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
char c = advance(lexer);
switch(c) {
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
case '[': return makeToken(lexer, TOKEN_BRACKET_LEFT);
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
case '&': //TOKEN_AND not used
if (advance(lexer) != '&') {
return makeErrorToken(lexer, "Unexpected '&'");
} else {
return makeToken(lexer, TOKEN_AND);
}
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
case ':': return makeToken(lexer, TOKEN_COLON);
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
case ',': return makeToken(lexer, TOKEN_COMMA);
case '.':
if (peek(lexer) == '.' && peekNext(lexer) == '.') {
advance(lexer);
advance(lexer);
return makeToken(lexer, TOKEN_REST);
}
return makeToken(lexer, TOKEN_DOT);
case '"':
return makeString(lexer, c);
//TODO: possibly support interpolated strings
default: {
char buffer[128];
snprintf(buffer, 128, "Unexpected token: %c", c);
return makeErrorToken(lexer, buffer);
}
}
}
static void trim(char** s, int* l) { //all this to remove a newline?
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
}
//for debugging
void printToken(Token* token) {
if (token->type == TOKEN_ERROR) {
printf(ERROR "Error\t%d\t%.*s\n" RESET, token->line, token->length, token->lexeme);
return;
}
printf("\t%d\t%d\t", token->type, token->line);
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
printf("%.*s\t", token->length, token->lexeme);
} else {
char* keyword = findKeywordByType(token->type);
if (keyword != NULL) {
printf("%s", keyword);
} else {
char* str = token->lexeme;
int length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
}
}
printf("\n");
}
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include "toy_common.h"
#include "token_types.h"
//lexers are bound to a string of code, and return a single token every time scan is called
typedef struct {
char* source;
int start; //start of the token
int current; //current position of the lexer
int line; //track this for error handling
} Lexer;
//tokens are intermediaries between lexers and parsers
typedef struct {
TokenType type;
char* lexeme;
int length;
int line;
} Token;
TOY_API void initLexer(Lexer* lexer, char* source);
Token scanLexer(Lexer* lexer);
//for debugging
void printToken(Token* token);
-703
View File
@@ -1,703 +0,0 @@
#include "literal.h"
#include "memory.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
#include "console_colors.h"
#include <stdio.h>
//hash util functions
static unsigned int hashString(const char* string, int length) {
unsigned int hash = 2166136261u;
for (int i = 0; i < length; i++) {
hash *= string[i];
hash ^= 16777619;
}
return hash;
}
static unsigned int hash(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
//exposed functions
void freeLiteral(Literal literal) {
if (IS_STRING(literal)) {
FREE_ARRAY(char, AS_STRING(literal), literal.as.string.length + 1);
return;
}
if (IS_ARRAY(literal) || literal.type == LITERAL_DICTIONARY_INTERMEDIATE || literal.type == LITERAL_TYPE_INTERMEDIATE) {
freeLiteralArray(AS_ARRAY(literal));
FREE(LiteralArray, AS_ARRAY(literal));
return;
}
if (IS_DICTIONARY(literal)) {
freeLiteralDictionary(AS_DICTIONARY(literal));
FREE(LiteralDictionary, AS_DICTIONARY(literal));
return;
}
if (IS_FUNCTION(literal)) {
popScope(AS_FUNCTION(literal).scope);
AS_FUNCTION(literal).scope = NULL;
FREE_ARRAY(unsigned char, AS_FUNCTION(literal).bytecode, AS_FUNCTION(literal).length);
}
if (IS_IDENTIFIER(literal)) {
FREE_ARRAY(char, AS_IDENTIFIER(literal), literal.as.identifier.length + 1);
return;
}
if (IS_TYPE(literal)) {
for (int i = 0; i < AS_TYPE(literal).count; i++) {
freeLiteral(((Literal*)(AS_TYPE(literal).subtypes))[i]);
}
FREE_ARRAY(Literal, AS_TYPE(literal).subtypes, AS_TYPE(literal).capacity);
return;
}
}
bool _isTruthy(Literal x) {
if (IS_NULL(x)) {
fprintf(stderr, ERROR "ERROR: Null is neither true nor false\n" RESET);
return false;
}
if (IS_BOOLEAN(x)) {
return AS_BOOLEAN(x);
}
return true;
}
Literal _toStringLiteral(char* str, int length) {
return ((Literal){LITERAL_STRING, { .string.ptr = (char*)str, .string.length = length }});
}
Literal _toIdentifierLiteral(char* str, int length) {
return ((Literal){LITERAL_IDENTIFIER,{ .identifier.ptr = (char*)str, .identifier.length = length, .identifier.hash = hashString(str, length) }});
}
Literal* _typePushSubtype(Literal* lit, Literal subtype) {
//grow the subtype array
if (AS_TYPE(*lit).count + 1 > AS_TYPE(*lit).capacity) {
int oldCapacity = AS_TYPE(*lit).capacity;
AS_TYPE(*lit).capacity = GROW_CAPACITY(oldCapacity);
AS_TYPE(*lit).subtypes = GROW_ARRAY(Literal, AS_TYPE(*lit).subtypes, oldCapacity, AS_TYPE(*lit).capacity);
}
//actually push
((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count++ ] = subtype;
return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ];
}
Literal copyLiteral(Literal original) {
switch(original.type) {
case LITERAL_NULL:
case LITERAL_BOOLEAN:
case LITERAL_INTEGER:
case LITERAL_FLOAT:
//no copying needed
return original;
case LITERAL_STRING: {
return TO_STRING_LITERAL(copyString(AS_STRING(original), original.as.string.length), original.as.string.length);
}
case LITERAL_ARRAY: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
//copy each element
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
pushLiteralArray(array, AS_ARRAY(original)->literals[i]);
}
return TO_ARRAY_LITERAL(array);
}
case LITERAL_DICTIONARY: {
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
//copy each entry
for (int i = 0; i < AS_DICTIONARY(original)->capacity; i++) {
if ( !IS_NULL(AS_DICTIONARY(original)->entries[i].key) ) {
setLiteralDictionary(dictionary, AS_DICTIONARY(original)->entries[i].key, AS_DICTIONARY(original)->entries[i].value);
}
}
return TO_DICTIONARY_LITERAL(dictionary);
}
case LITERAL_FUNCTION: {
unsigned char* buffer = ALLOCATE(unsigned char, AS_FUNCTION(original).length);
memcpy(buffer, AS_FUNCTION(original).bytecode, AS_FUNCTION(original).length);
Literal literal = TO_FUNCTION_LITERAL(buffer, AS_FUNCTION(original).length);
AS_FUNCTION(literal).scope = copyScope(AS_FUNCTION(original).scope);
return literal;
}
case LITERAL_IDENTIFIER: {
return TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(original), original.as.identifier.length), original.as.identifier.length);
}
case LITERAL_TYPE: {
Literal lit = TO_TYPE_LITERAL(AS_TYPE(original).typeOf, AS_TYPE(original).constant);
for (int i = 0; i < AS_TYPE(original).count; i++) {
TYPE_PUSH_SUBTYPE(&lit, copyLiteral( ((Literal*)(AS_TYPE(original).subtypes))[i] ));
}
return lit;
}
case LITERAL_OPAQUE: {
return original; //literally a shallow copy
}
case LITERAL_DICTIONARY_INTERMEDIATE: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
//copy each element
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
pushLiteralArray(array, literal);
freeLiteral(literal);
}
Literal ret = TO_ARRAY_LITERAL(array);
ret.type = LITERAL_DICTIONARY_INTERMEDIATE;
return ret;
}
case LITERAL_TYPE_INTERMEDIATE: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
//copy each element
for (int i = 0; i < AS_ARRAY(original)->count; i++) {
Literal literal = copyLiteral(AS_ARRAY(original)->literals[i]);
pushLiteralArray(array, literal);
freeLiteral(literal);
}
Literal ret = TO_ARRAY_LITERAL(array);
ret.type = LITERAL_TYPE_INTERMEDIATE;
return ret;
}
case LITERAL_FUNCTION_INTERMEDIATE: //caries a compiler
case LITERAL_FUNCTION_NATIVE:
//no copying possible
return original;
default:
fprintf(stderr, ERROR "ERROR: Can't copy that literal type: %d\n" RESET, original.type);
return TO_NULL_LITERAL;
}
}
char* copyString(char* original, int length) {
//make a local copy of the char array
char* buffer = ALLOCATE(char, length + 1);
strncpy(buffer, original, length);
buffer[length] = '\0';
return buffer;
}
bool literalsAreEqual(Literal lhs, Literal rhs) {
//utility for other things
if (lhs.type != rhs.type) {
// ints and floats are compatible
if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) {
if (IS_INTEGER(lhs)) {
return AS_INTEGER(lhs) + AS_FLOAT(rhs);
}
else {
return AS_FLOAT(lhs) + AS_INTEGER(rhs);
}
}
return false;
}
switch(lhs.type) {
case LITERAL_NULL:
return true; //can only be true because of the check above
case LITERAL_BOOLEAN:
return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs);
case LITERAL_INTEGER:
return AS_INTEGER(lhs) == AS_INTEGER(rhs);
case LITERAL_FLOAT:
return AS_FLOAT(lhs) == AS_FLOAT(rhs);
case LITERAL_STRING:
if (lhs.as.string.length != rhs.as.string.length) {
return false;
}
return !strncmp(AS_STRING(lhs), AS_STRING(rhs), lhs.as.string.length);
case LITERAL_ARRAY:
case LITERAL_DICTIONARY_INTERMEDIATE: //BUGFIX
case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array
//mismatched sizes
if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) {
return false;
}
//mismatched elements (in order)
for (int i = 0; i < AS_ARRAY(lhs)->count; i++) {
if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) {
return false;
}
}
return true;
case LITERAL_DICTIONARY:
//relatively slow, especially when nested
for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) {
if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys
//check it exists in rhs
if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) {
return false;
}
//compare the values
Literal val = getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key); //TODO: could be more efficient
if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, val)) {
freeLiteral(val);
return false;
}
freeLiteral(val);
}
}
return true;
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
return false; //functions are never equal
break;
case LITERAL_IDENTIFIER:
//check shortcuts
if (HASH_I(lhs) != HASH_I(rhs) && lhs.as.identifier.length != rhs.as.identifier.length) {
return false;
}
return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), lhs.as.identifier.length);
case LITERAL_TYPE:
//check types
if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) {
return false;
}
//const don't match
if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) {
return false;
}
//check subtypes
if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) {
return false;
}
//check array|dictionary signatures are the same (in order)
if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) {
for (int i = 0; i < AS_TYPE(lhs).count; i++) {
if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) {
return false;
}
}
}
return true;
case LITERAL_OPAQUE:
return false; //IDK what this is!
case LITERAL_ANY:
return true;
case LITERAL_FUNCTION_INTERMEDIATE:
fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET);
return false;
default:
//should never be seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type);
return false;
}
return false;
}
int hashLiteral(Literal lit) {
switch(lit.type) {
case LITERAL_NULL:
return 0;
case LITERAL_BOOLEAN:
return AS_BOOLEAN(lit) ? 1 : 0;
case LITERAL_INTEGER:
return hash((unsigned int)AS_INTEGER(lit));
case LITERAL_FLOAT:
return hash(*(unsigned int*)(&AS_FLOAT(lit)));
case LITERAL_STRING:
return hashString(AS_STRING(lit), strlen(AS_STRING(lit)));
case LITERAL_ARRAY: {
unsigned int res = 0;
for (int i = 0; i < AS_ARRAY(lit)->count; i++) {
res += hashLiteral(AS_ARRAY(lit)->literals[i]);
}
return hash(res);
}
case LITERAL_DICTIONARY: {
unsigned int res = 0;
for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) {
if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key);
res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value);
}
}
return hash(res);
}
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
return 0; //TODO: find a way to hash these properly
case LITERAL_IDENTIFIER:
return HASH_I(lit); //pre-computed
case LITERAL_TYPE:
return AS_TYPE(lit).typeOf; //nothing else I can do
case LITERAL_OPAQUE:
case LITERAL_ANY:
return -1;
default:
//should never bee seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type);
return 0;
}
}
//utils
static void stdoutWrapper(const char* output) {
printf("%s", output);
}
//buffer the prints
static char* globalPrintBuffer = NULL;
static size_t globalPrintCapacity = 0;
static size_t globalPrintCount = 0;
//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else
static char quotes = 0; //set to 0 to not show string quotes
static void printToBuffer(const char* str) {
while (strlen(str) + globalPrintCount + 1 > globalPrintCapacity) {
int oldCapacity = globalPrintCapacity;
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
globalPrintCount += strlen(str);
}
//exposed functions
void printLiteral(Literal literal) {
printLiteralCustom(literal, stdoutWrapper);
}
void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
switch(literal.type) {
case LITERAL_NULL:
printFn("null");
break;
case LITERAL_BOOLEAN:
printFn(AS_BOOLEAN(literal) ? "true" : "false");
break;
case LITERAL_INTEGER: {
char buffer[256];
snprintf(buffer, 256, "%d", AS_INTEGER(literal));
printFn(buffer);
}
break;
case LITERAL_FLOAT: {
char buffer[256];
snprintf(buffer, 256, "%g", AS_FLOAT(literal));
printFn(buffer);
}
break;
case LITERAL_STRING: {
char buffer[MAX_STRING_LENGTH];
if (!quotes) {
snprintf(buffer, MAX_STRING_LENGTH, "%.*s", (int)strlen(AS_STRING(literal)), AS_STRING(literal));
}
else {
snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)strlen(AS_STRING(literal)), AS_STRING(literal), quotes);
}
printFn(buffer);
}
break;
case LITERAL_ARRAY: {
LiteralArray* ptr = AS_ARRAY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
printToBuffer("[");
for (int i = 0; i < ptr->count; i++) {
quotes = '"';
printLiteralCustom(ptr->literals[i], printToBuffer);
if (i + 1 < ptr->count) {
printToBuffer(",");
}
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case LITERAL_DICTIONARY: {
LiteralDictionary* ptr = AS_DICTIONARY(literal);
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
int delimCount = 0;
printToBuffer("[");
for (int i = 0; i < ptr->capacity; i++) {
if (IS_NULL(ptr->entries[i].key)) {
continue;
}
if (delimCount++ > 0) {
printToBuffer(",");
}
quotes = '"';
printLiteralCustom(ptr->entries[i].key, printToBuffer);
printToBuffer(":");
quotes = '"';
printLiteralCustom(ptr->entries[i].value, printToBuffer);
}
//empty dicts MUST have a ":" printed
if (ptr->count == 0) {
printToBuffer(":");
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
//TODO: functions
case LITERAL_FUNCTION:
case LITERAL_FUNCTION_NATIVE:
printFn("(function)");
break;
case LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", (int)strlen( AS_IDENTIFIER(literal) ), AS_IDENTIFIER(literal));
printFn(buffer);
}
break;
case LITERAL_TYPE: {
//hold potential parent-call buffers on the C stack
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the type correctly
printToBuffer("<");
switch(AS_TYPE(literal).typeOf) {
case LITERAL_NULL:
printToBuffer("null");
break;
case LITERAL_BOOLEAN:
printToBuffer("bool");
break;
case LITERAL_INTEGER:
printToBuffer("int");
break;
case LITERAL_FLOAT:
printToBuffer("float");
break;
case LITERAL_STRING:
printToBuffer("string");
break;
case LITERAL_ARRAY:
//print all in the array
printToBuffer("[");
for (int i = 0; i < AS_TYPE(literal).count; i++) {
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
}
printToBuffer("]");
break;
case LITERAL_DICTIONARY:
printToBuffer("[");
for (int i = 0; i < AS_TYPE(literal).count; i += 2) {
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer);
printToBuffer(":");
printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer);
}
printToBuffer("]");
break;
case LITERAL_FUNCTION:
printToBuffer("function");
break;
case LITERAL_FUNCTION_NATIVE:
printToBuffer("native");
break;
case LITERAL_IDENTIFIER:
printToBuffer("identifier");
break;
case LITERAL_TYPE:
printToBuffer("type");
break;
case LITERAL_OPAQUE:
printToBuffer("opaque");
break;
case LITERAL_ANY:
printToBuffer("any");
break;
default:
//should never be seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf);
}
//const (printed last)
if (AS_TYPE(literal).constant) {
printToBuffer(" const");
}
printToBuffer(">");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
quotes = 0;
}
break;
case LITERAL_TYPE_INTERMEDIATE:
case LITERAL_FUNCTION_INTERMEDIATE:
printFn("Unprintable literal found");
break;
case LITERAL_OPAQUE:
printFn("(opaque)");
break;
case LITERAL_ANY:
printFn("(any)");
break;
default:
//should never be seen
fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type);
}
}
-128
View File
@@ -1,128 +0,0 @@
#pragma once
#include "toy_common.h"
#include <string.h>
typedef enum {
LITERAL_NULL,
LITERAL_BOOLEAN,
LITERAL_INTEGER,
LITERAL_FLOAT,
LITERAL_STRING,
LITERAL_ARRAY,
LITERAL_DICTIONARY,
LITERAL_FUNCTION,
LITERAL_IDENTIFIER,
LITERAL_TYPE,
LITERAL_OPAQUE,
LITERAL_ANY,
//these are meta-level types - not for general use
LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only
LITERAL_DICTIONARY_INTERMEDIATE, //used to process dictionaries in the compiler only
LITERAL_FUNCTION_INTERMEDIATE, //used to process functions in the compiler only
LITERAL_FUNCTION_ARG_REST, //used to process function rest parameters only
LITERAL_FUNCTION_NATIVE, //for handling native functions only
} LiteralType;
typedef struct {
LiteralType type;
union {
bool boolean;
int integer;
float number;
struct {
char* ptr;
int length;
} string;
void* array;
void* dictionary;
struct {
void* bytecode;
void* scope;
int length;
} function;
struct { //for variable names
char* ptr;
int length;
int hash;
} identifier;
struct {
LiteralType typeOf; //no longer a mask
bool constant;
void* subtypes; //for nested types caused by compounds
int capacity;
int count;
} type;
struct {
void* ptr;
int tag;
} opaque;
} as;
} Literal;
#define IS_NULL(value) ((value).type == LITERAL_NULL)
#define IS_BOOLEAN(value) ((value).type == LITERAL_BOOLEAN)
#define IS_INTEGER(value) ((value).type == LITERAL_INTEGER)
#define IS_FLOAT(value) ((value).type == LITERAL_FLOAT)
#define IS_STRING(value) ((value).type == LITERAL_STRING)
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
#define IS_OPAQUE(value) ((value).type == LITERAL_OPAQUE)
#define AS_BOOLEAN(value) ((value).as.boolean)
#define AS_INTEGER(value) ((value).as.integer)
#define AS_FLOAT(value) ((value).as.number)
#define AS_STRING(value) ((value).as.string.ptr)
#define AS_ARRAY(value) ((LiteralArray*)((value).as.array))
#define AS_DICTIONARY(value) ((LiteralDictionary*)((value).as.dictionary))
#define AS_FUNCTION(value) ((value).as.function)
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
#define AS_TYPE(value) ((value).as.type)
#define AS_OPAQUE(value) ((value).as.opaque.ptr)
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
#define TO_STRING_LITERAL(value, l) _toStringLiteral(value, l)
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
#define TO_IDENTIFIER_LITERAL(value, l) _toIdentifierLiteral(value, l)
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
#define TO_OPAQUE_LITERAL(value, t) ((Literal){ LITERAL_OPAQUE, { .opaque.ptr = value, .opaque.tag = t }})
TOY_API void freeLiteral(Literal literal);
#define IS_TRUTHY(x) _isTruthy(x)
#define MAX_STRING_LENGTH 4096
#define HASH_I(lit) ((lit).as.identifier.hash)
#define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype)
#define OPAQUE_TAG(o) o.as.opaque.tag
//BUGFIX: macros are not functions
TOY_API bool _isTruthy(Literal x);
TOY_API Literal _toStringLiteral(char* str, int length);
TOY_API Literal _toIdentifierLiteral(char* str, int length);
TOY_API Literal* _typePushSubtype(Literal* lit, Literal subtype);
//utils
TOY_API Literal copyLiteral(Literal original);
TOY_API char* copyString(char* original, int length);
TOY_API bool literalsAreEqual(Literal lhs, Literal rhs);
TOY_API int hashLiteral(Literal lit);
TOY_API void printLiteral(Literal literal);
TOY_API void printLiteralCustom(Literal literal, void (printFn)(const char*));
-100
View File
@@ -1,100 +0,0 @@
#include "literal_array.h"
#include "memory.h"
#include <stdio.h>
#include <string.h>
//exposed functions
void initLiteralArray(LiteralArray* array) {
array->capacity = 0;
array->count = 0;
array->literals = NULL;
}
void freeLiteralArray(LiteralArray* array) {
//clean up memory
for(int i = 0; i < array->count; i++) {
freeLiteral(array->literals[i]);
}
FREE_ARRAY(Literal, array->literals, array->capacity);
initLiteralArray(array);
}
int pushLiteralArray(LiteralArray* array, Literal literal) {
if (array->capacity < array->count + 1) {
int oldCapacity = array->capacity;
array->capacity = GROW_CAPACITY(oldCapacity);
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
}
array->literals[array->count] = copyLiteral(literal);
return array->count++;
}
Literal popLiteralArray(LiteralArray* array) {
if (array->count <= 0) {
return TO_NULL_LITERAL;
}
//get the return
Literal ret = array->literals[array->count-1];
//null the existing data
array->literals[array->count-1] = TO_NULL_LITERAL;
array->count--;
return ret;
}
//find a literal in the array that matches the "literal" argument
int findLiteralIndex(LiteralArray* array, Literal literal) {
for (int i = 0; i < array->count; i++) {
//not the same type
if (array->literals[i].type != literal.type) {
continue;
}
//types match?
if (literalsAreEqual(array->literals[i], literal)) {
return i;
}
}
return -1;
}
bool setLiteralArray(LiteralArray* array, Literal index, Literal value) {
if (!IS_INTEGER(index)) {
return false;
}
int idx = AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return false;
}
//TODO: implicit push when referencing one-past-the-end?
freeLiteral(array->literals[idx]);
array->literals[idx] = copyLiteral(value);
return true;
}
Literal getLiteralArray(LiteralArray* array, Literal index) {
if (!IS_INTEGER(index)) {
return TO_NULL_LITERAL;
}
int idx = AS_INTEGER(index);
if (idx < 0 || idx >= array->count) {
return TO_NULL_LITERAL;
}
return copyLiteral(array->literals[idx]);
}
-20
View File
@@ -1,20 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
typedef struct LiteralArray {
Literal* literals;
int capacity;
int count;
} LiteralArray;
TOY_API void initLiteralArray(LiteralArray* array);
TOY_API void freeLiteralArray(LiteralArray* array);
TOY_API int pushLiteralArray(LiteralArray* array, Literal literal);
TOY_API Literal popLiteralArray(LiteralArray* array);
TOY_API bool setLiteralArray(LiteralArray* array, Literal index, Literal value);
TOY_API Literal getLiteralArray(LiteralArray* array, Literal index);
int findLiteralIndex(LiteralArray* array, Literal literal);
-219
View File
@@ -1,219 +0,0 @@
#include "literal_dictionary.h"
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
//util functions
static void setEntryValues(_entry* entry, Literal key, Literal value) {
//much simpler now
freeLiteral(entry->key);
entry->key = copyLiteral(key);
freeLiteral(entry->value);
entry->value = copyLiteral(value);
}
static _entry* getEntryArray(_entry* array, int capacity, Literal key, unsigned int hash, bool mustExist) {
//find "key", starting at index
unsigned int index = hash % capacity;
unsigned int start = index;
//increment once, so it can't equal start
index = (index + 1) % capacity;
//literal probing and collision checking
while (index != start) { //WARNING: this is the only function allowed to retrieve an entry from the array
_entry* entry = &array[index];
if (IS_NULL(entry->key)) { //if key is empty, it's either empty or tombstone
if (IS_NULL(entry->value) && !mustExist) {
//found a truly empty bucket
return entry;
}
//else it's a tombstone - ignore
} else {
if (literalsAreEqual(key, entry->key)) {
return entry;
}
}
index = (index + 1) % capacity;
}
return NULL;
}
static void adjustEntryCapacity(_entry** dictionaryHandle, int oldCapacity, int capacity) {
//new entry space
_entry* newEntries = ALLOCATE(_entry, capacity);
for (int i = 0; i < capacity; i++) {
newEntries[i].key = TO_NULL_LITERAL;
newEntries[i].value = TO_NULL_LITERAL;
}
//move the old array into the new one
for (int i = 0; i < oldCapacity; i++) {
if (IS_NULL((*dictionaryHandle)[i].key)) {
continue;
}
//place the key and value in the new array (reusing string memory)
_entry* entry = getEntryArray(newEntries, capacity, TO_NULL_LITERAL, hashLiteral((*dictionaryHandle)[i].key), false);
entry->key = (*dictionaryHandle)[i].key;
entry->value = (*dictionaryHandle)[i].value;
}
//clear the old array
FREE_ARRAY(_entry, *dictionaryHandle, oldCapacity);
*dictionaryHandle = newEntries;
}
static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int contains, Literal key, Literal value, int hash) {
//expand array if needed
if (contains + 1 > *capacityPtr * DICTIONARY_MAX_LOAD) {
int oldCapacity = *capacityPtr;
*capacityPtr = GROW_CAPACITY(*capacityPtr);
adjustEntryCapacity(dictionaryHandle, oldCapacity, *capacityPtr); //custom rather than automatic reallocation
}
_entry* entry = getEntryArray(*dictionaryHandle, *capacityPtr, key, hash, false);
//true = contains increase
if (IS_NULL(entry->key)) {
setEntryValues(entry, key, value);
return true;
}
else {
setEntryValues(entry, key, value);
return false;
}
return false;
}
static void freeEntry(_entry* entry) {
freeLiteral(entry->key);
freeLiteral(entry->value);
entry->key = TO_NULL_LITERAL;
entry->value = TO_NULL_LITERAL;
}
static void freeEntryArray(_entry* array, int capacity) {
if (array == NULL) {
return;
}
for (int i = 0; i < capacity; i++) {
if (!IS_NULL(array[i].key)) {
freeEntry(&array[i]);
}
}
FREE_ARRAY(_entry, array, capacity);
}
//exposed functions
void initLiteralDictionary(LiteralDictionary* dictionary) {
//HACK: because modulo by 0 is undefined, set the capacity to a non-zero value (and allocate the arrays)
dictionary->entries = NULL;
dictionary->capacity = GROW_CAPACITY(0);
dictionary->contains = 0;
dictionary->count = 0;
adjustEntryCapacity(&dictionary->entries, 0, dictionary->capacity);
}
void freeLiteralDictionary(LiteralDictionary* dictionary) {
freeEntryArray(dictionary->entries, dictionary->capacity);
dictionary->capacity = 0;
dictionary->contains = 0;
}
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
return;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
return;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
return;
}
const int increment = setEntryArray(&dictionary->entries, &dictionary->capacity, dictionary->contains, key, value, hashLiteral(key));
if (increment) {
dictionary->contains++;
dictionary->count++;
}
}
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
return TO_NULL_LITERAL;
}
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
if (entry != NULL) {
return copyLiteral(entry->value);
}
else {
return TO_NULL_LITERAL;
}
}
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
if (IS_NULL(key)) {
fprintf(stderr, ERROR "Dictionaries can't have null keys (remove)\n" RESET);
return;
}
//BUGFIX: Can't hash a function
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have function keys (remove)\n" RESET);
return;
}
if (IS_OPAQUE(key)) {
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
return;
}
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
if (entry != NULL) {
freeEntry(entry);
entry->value = TO_BOOLEAN_LITERAL(true); //tombstone
dictionary->count--;
}
}
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
//null & not tombstoned
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
}
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include "toy_common.h"
#include "literal.h"
//TODO: benchmark this
#define DICTIONARY_MAX_LOAD 0.75
typedef struct _entry {
Literal key;
Literal value;
} _entry;
typedef struct LiteralDictionary {
_entry* entries;
int capacity;
int count;
int contains; //count + tombstones, for internal use
} LiteralDictionary;
TOY_API void initLiteralDictionary(LiteralDictionary* dictionary);
TOY_API void freeLiteralDictionary(LiteralDictionary* dictionary);
TOY_API void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
TOY_API Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
TOY_API void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
TOY_API bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);
-48
View File
@@ -1,48 +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)
$(OBJ): | $(ODIR)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) $(ODIR)
-29
View File
@@ -1,29 +0,0 @@
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
#include <stdlib.h>
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
if (newSize == 0 && oldSize == 0) {
//causes issues, so just skip out with a NO-OP
return NULL;
}
if (newSize == 0) {
free(pointer);
return NULL;
}
void* mem = realloc(pointer, newSize);
if (mem == NULL) {
fprintf(stderr, ERROR "[internal]Memory allocation error (requested %d for %ld, replacing %d)\n" ERROR, (int)newSize, (long int)pointer, (int)oldSize);
exit(-1);
}
return mem;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "toy_common.h"
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
#define GROW_CAPACITY_FAST(capacity) ((capacity) < 32 ? 32 : (capacity) * 2)
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
-84
View File
@@ -1,84 +0,0 @@
#pragma once
typedef enum Opcode {
OP_EOF,
//basic statements
OP_ASSERT,
OP_PRINT,
//data
OP_LITERAL,
OP_LITERAL_LONG, //for more than 256 literals in a chunk
OP_LITERAL_RAW, //forcibly get the raw value of the literal
//arithmetic operators
OP_NEGATE,
OP_ADDITION,
OP_SUBTRACTION,
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
OP_GROUPING_BEGIN,
OP_GROUPING_END,
//variable stuff
OP_SCOPE_BEGIN,
OP_SCOPE_END,
OP_TYPE_DECL, //declare a type to be used (as a literal)
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
OP_VAR_DECL, //declare a variable to be used (as a literal)
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
OP_FN_DECL, //declare a function to be used (as a literal)
OP_FN_DECL_LONG, //declare a function to be used (as a long literal)
OP_VAR_ASSIGN, //assign to a literal
OP_VAR_ADDITION_ASSIGN,
OP_VAR_SUBTRACTION_ASSIGN,
OP_VAR_MULTIPLICATION_ASSIGN,
OP_VAR_DIVISION_ASSIGN,
OP_VAR_MODULO_ASSIGN,
OP_TYPE_CAST, //temporarily change a type of an atomic value
OP_TYPE_OF, //get the type of a variable
OP_IMPORT,
OP_EXPORT,
//for indexing
OP_INDEX,
OP_INDEX_ASSIGN,
OP_INDEX_ASSIGN_INTERMEDIATE,
OP_DOT,
//comparison of values
OP_COMPARE_EQUAL,
OP_COMPARE_NOT_EQUAL,
OP_COMPARE_LESS,
OP_COMPARE_LESS_EQUAL,
OP_COMPARE_GREATER,
OP_COMPARE_GREATER_EQUAL,
OP_INVERT, //for booleans
//logical operators
OP_AND,
OP_OR,
//jumps, and conditional jumps (absolute)
OP_JUMP,
OP_IF_FALSE_JUMP,
OP_FN_CALL,
OP_FN_RETURN,
//pop the stack at the end of a complex statement
OP_POP_STACK,
//meta
OP_FN_END, //different from SECTION_END
OP_SECTION_END = 255,
//TODO: add more
} Opcode;
-1783
View File
File diff suppressed because it is too large Load Diff
-20
View File
@@ -1,20 +0,0 @@
#pragma once
#include "toy_common.h"
#include "lexer.h"
#include "ast_node.h"
//DOCS: parsers are bound to a lexer, and turn the outputted tokens into AST nodes
typedef struct {
Lexer* lexer;
bool error; //I've had an error
bool panic; //I am processing an error
//track the last two outputs from the lexer
Token current;
Token previous;
} Parser;
TOY_API void initParser(Parser* parser, Lexer* lexer);
TOY_API void freeParser(Parser* parser);
TOY_API ASTNode* scanParser(Parser* parser);
-303
View File
@@ -1,303 +0,0 @@
#include "scope.h"
#include "memory.h"
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Scope* scope) {
scope->references--;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
}
if (scope->references > 0) {
return;
}
freeLiteralDictionary(&scope->variables);
freeLiteralDictionary(&scope->types);
FREE(Scope, scope);
}
//return false if invalid type
static bool checkType(Literal typeLiteral, Literal original, Literal value, bool constCheck) {
//for constants, fail if original != value
if (constCheck && AS_TYPE(typeLiteral).constant && !literalsAreEqual(original, value)) {
return false;
}
//for any types
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
return true;
}
//don't allow null types
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
return false;
}
//always allow null values
if (IS_NULL(value)) {
return true;
}
//for each type, if a mismatch is found, return false
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ARRAY && !IS_ARRAY(value)) {
return false;
}
if (IS_ARRAY(value)) {
//check value's type
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
return false;
}
//if null, assume it's a new entry
if (IS_NULL(original)) {
return true;
}
//check children
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
if (AS_ARRAY(original)->count <= i) {
return true; //assume new entry pushed
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(original)->literals[i], AS_ARRAY(value)->literals[i], constCheck)) {
return false;
}
}
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_DICTIONARY && !IS_DICTIONARY(value)) {
return false;
}
if (IS_DICTIONARY(value)) {
//check value's type
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
return false;
}
//if null, assume it's a new entry to a parent
if (IS_NULL(original)) {
return true;
}
//check each child of value against the child of original
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
if (IS_NULL(AS_DICTIONARY(value)->entries[i].key)) { //only non-tombstones
continue;
}
//find the internal child of original that matches this child of value
_entry* ptr = NULL;
for (int j = 0; j < AS_DICTIONARY(original)->capacity; j++) {
if (literalsAreEqual(AS_DICTIONARY(original)->entries[j].key, AS_DICTIONARY(value)->entries[i].key)) {
ptr = &AS_DICTIONARY(original)->entries[j];
break;
}
}
//if not found, assume it's a new entry
if (!ptr) {
continue;
}
//check the type of key and value
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], ptr->key, AS_DICTIONARY(value)->entries[i].key, constCheck)) {
return false;
}
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], ptr->value, AS_DICTIONARY(value)->entries[i].value, constCheck)) {
return false;
}
}
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FUNCTION && !IS_FUNCTION(value)) {
return false;
}
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
return false;
}
return true;
}
//exposed functions
Scope* pushScope(Scope* ancestor) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = ancestor;
initLiteralDictionary(&scope->variables);
initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
return scope;
}
Scope* popScope(Scope* scope) {
if (scope == NULL) { //CAN pop a null
return NULL;
}
Scope* ret = scope->ancestor;
//BUGFIX: when freeing a scope, free the function's scopes manually
for (int i = 0; i < scope->variables.capacity; i++) {
//handle keys, just in case
if (IS_FUNCTION(scope->variables.entries[i].key)) {
popScope(AS_FUNCTION(scope->variables.entries[i].key).scope);
AS_FUNCTION(scope->variables.entries[i].key).scope = NULL;
}
if (IS_FUNCTION(scope->variables.entries[i].value)) {
popScope(AS_FUNCTION(scope->variables.entries[i].value).scope);
AS_FUNCTION(scope->variables.entries[i].value).scope = NULL;
}
}
freeAncestorChain(scope);
return ret;
}
Scope* copyScope(Scope* original) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = original->ancestor;
initLiteralDictionary(&scope->variables);
initLiteralDictionary(&scope->types);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
ptr->references++;
}
//copy the contents of the dictionaries
for (int i = 0; i < original->variables.capacity; i++) {
if (!IS_NULL(original->variables.entries[i].key)) {
setLiteralDictionary(&scope->variables, original->variables.entries[i].key, original->variables.entries[i].value);
}
}
for (int i = 0; i < original->types.capacity; i++) {
if (!IS_NULL(original->types.entries[i].key)) {
setLiteralDictionary(&scope->types, original->types.entries[i].key, original->types.entries[i].value);
}
}
return scope;
}
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
//don't redefine a variable within this scope
if (existsLiteralDictionary(&scope->variables, key)) {
return false;
}
//store the type, for later checking on assignment
setLiteralDictionary(&scope->types, key, type);
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
return true;
}
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return isDelcaredScopeVariable(scope->ancestor, key);
}
return true;
}
//return false if undefined, or can't be assigned
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return setScopeVariable(scope->ancestor, key, value, constCheck);
}
//type checking
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
Literal original = getLiteralDictionary(&scope->variables, key);
if (!checkType(typeLiteral, original, value, constCheck)) {
freeLiteral(typeLiteral);
freeLiteral(original);
return false;
}
//actually assign
setLiteralDictionary(&scope->variables, key, value);
freeLiteral(typeLiteral);
freeLiteral(original);
return true;
}
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = getLiteralDictionary(&scope->variables, key);
return true;
}
Literal getScopeType(Scope* scope, Literal key) {
//dead end
if (scope == NULL) {
return TO_NULL_LITERAL;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->types, key)) {
return getScopeType(scope->ancestor, key);
}
return getLiteralDictionary(&scope->types, key);
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include "literal_array.h"
#include "literal_dictionary.h"
typedef struct Scope {
LiteralDictionary variables; //only allow identifiers as the keys
LiteralDictionary types; //the types, indexed by identifiers
struct Scope* ancestor;
int references; //how many scopes point here
} Scope;
Scope* pushScope(Scope* scope);
Scope* popScope(Scope* scope);
Scope* copyScope(Scope* original);
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key, Literal type);
bool isDelcaredScopeVariable(Scope* scope, Literal key);
//return false if undefined
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck);
bool getScopeVariable(Scope* scope, Literal key, Literal* value);
Literal getScopeType(Scope* scope, Literal key);
-92
View File
@@ -1,92 +0,0 @@
#pragma once
typedef enum TokenType {
//types
TOKEN_NULL,
TOKEN_BOOLEAN,
TOKEN_INTEGER,
TOKEN_FLOAT,
TOKEN_STRING,
TOKEN_ARRAY,
TOKEN_DICTIONARY,
TOKEN_FUNCTION,
TOKEN_OPAQUE,
TOKEN_ANY,
//keywords and reserved words
TOKEN_AS,
TOKEN_ASSERT,
TOKEN_BREAK,
TOKEN_CLASS,
TOKEN_CONST,
TOKEN_CONTINUE,
TOKEN_DO,
TOKEN_ELSE,
TOKEN_EXPORT,
TOKEN_FOR,
TOKEN_FOREACH,
TOKEN_IF,
TOKEN_IMPORT,
TOKEN_IN,
TOKEN_OF,
TOKEN_PRINT,
TOKEN_RETURN,
TOKEN_TYPE,
TOKEN_ASTYPE,
TOKEN_TYPEOF,
TOKEN_VAR,
TOKEN_WHILE,
//literal values
TOKEN_IDENTIFIER,
TOKEN_LITERAL_TRUE,
TOKEN_LITERAL_FALSE,
TOKEN_LITERAL_INTEGER,
TOKEN_LITERAL_FLOAT,
TOKEN_LITERAL_STRING,
//math operators
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_MULTIPLY,
TOKEN_DIVIDE,
TOKEN_MODULO,
TOKEN_PLUS_ASSIGN,
TOKEN_MINUS_ASSIGN,
TOKEN_MULTIPLY_ASSIGN,
TOKEN_DIVIDE_ASSIGN,
TOKEN_MODULO_ASSIGN,
TOKEN_PLUS_PLUS,
TOKEN_MINUS_MINUS,
TOKEN_ASSIGN,
//logical operators
TOKEN_PAREN_LEFT,
TOKEN_PAREN_RIGHT,
TOKEN_BRACKET_LEFT,
TOKEN_BRACKET_RIGHT,
TOKEN_BRACE_LEFT,
TOKEN_BRACE_RIGHT,
TOKEN_NOT,
TOKEN_NOT_EQUAL,
TOKEN_EQUAL,
TOKEN_LESS,
TOKEN_GREATER,
TOKEN_LESS_EQUAL,
TOKEN_GREATER_EQUAL,
TOKEN_AND,
TOKEN_OR,
//other operators
TOKEN_COLON,
TOKEN_SEMICOLON,
TOKEN_COMMA,
TOKEN_DOT,
TOKEN_PIPE,
TOKEN_REST,
//meta tokens
TOKEN_PASS,
TOKEN_ERROR,
TOKEN_EOF,
} TokenType;
-125
View File
@@ -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 initCommand(int argc, const char* argv[]) {
//default values
command.error = false;
command.help = false;
command.version = false;
command.binaryfile = NULL;
command.sourcefile = NULL;
command.compilefile = NULL;
command.outfile = "out.tb";
command.source = NULL;
command.verbose = false;
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
command.error = true; //error state by default, set to false by successful flags
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
command.help = true;
command.error = false;
continue;
}
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
command.version = true;
command.error = false;
continue;
}
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
command.verbose = true;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--sourcefile")) && i + 1 < argc) {
command.sourcefile = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
command.source = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compile")) && i + 1 < argc) {
command.compilefile = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
if ((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i + 1 < argc) {
command.outfile = (char*)argv[i + 1];
i++;
command.error = false;
continue;
}
//option without a flag + ending in .tb = binary input
if (i < argc) {
if (strncmp(&(argv[i][strlen(argv[i]) - 3]), ".tb", 3) == 0) {
command.binaryfile = (char*)argv[i];
command.error = false;
continue;
}
}
//don't keep reading in an error state
return;
}
}
void usageCommand(int argc, const char* argv[]) {
printf("Usage: %s [<file.tb> | -h | -v | [-d][-f file | -i source | -c file [-o outfile]]]\n\n", argv[0]);
}
void helpCommand(int argc, const char* argv[]) {
usageCommand(argc, argv);
printf("<file.tb>\t\t\tBinary input file in tb format, must be version %d.%d.%d.\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
printf("-h\t| --help\t\tShow this help then exit.\n\n");
printf("-v\t| --version\t\tShow version and copyright information then exit.\n\n");
printf("-d\t| --debug\t\tBe verbose when operating.\n\n");
printf("-f\t| --file filename\tParse, compile and execute the source file.\n\n");
printf("-i\t| --input source\tParse, compile and execute this given string of source code.\n\n");
printf("-c\t| --compile filename\tParse and compile the specified source file into an output file.\n\n");
printf("-o\t| --output outfile\tName of the output file built with --compile (default: out.tb).\n\n");
}
void copyrightCommand(int argc, const char* argv[]) {
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
}
#endif
-54
View File
@@ -1,54 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define TOY_VERSION_MAJOR 0
#define TOY_VERSION_MINOR 6
#define TOY_VERSION_PATCH 2
#define TOY_VERSION_BUILD __DATE__ " " __TIME__
//platform exports/imports
#if defined(__linux__)
#define TOY_API extern
#include <time.h>
#include <sys/time.h>
#elif defined(_WIN32) || defined(WIN32)
#define TOY_API
#include <time.h>
#include <sys/time.h>
#else
#define TOY_API
#include <time.h>
#include <sys/time.h>
#endif
#ifndef TOY_EXPORT
//for processing the command line arguments
typedef struct {
bool error;
bool help;
bool version;
char* binaryfile;
char* sourcefile;
char* compilefile;
char* outfile; //defaults to out.tb
char* source;
bool verbose;
} Command;
extern Command command;
void initCommand(int argc, const char* argv[]);
void usageCommand(int argc, const char* argv[]);
void helpCommand(int argc, const char* argv[]);
void copyrightCommand(int argc, const char* argv[]);
#endif
//NOTE: assigning to a byte from a short loses data
#define AS_USHORT(value) (*(unsigned short*)(&(value)))
-38
View File
@@ -1,38 +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)
TESTS = $(wildcard *.c)
OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$(TESTS:.c=.o))
.PRECIOUS: $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
all: $(OBJ) $(TESTS:%.c=../$(TOY_OUTDIR)/%.exe)
../$(TOY_OUTDIR)/%.exe: $(ODIR)/%.o
@$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS)
ifeq ($(shell uname),Linux)
valgrind --leak-check=full --track-origins=yes $@
else
$@
endif
$(OBJ): | $(ODIR)
$(ODIR):
mkdir $(ODIR)
$(ODIR)/%.o: %.c
@$(CC) -c -o $@ $< $(CFLAGS)
$(ODIR)/%.o: ../source/%.c
@$(CC) -c -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
$(RM) $(ODIR)
-31
View File
@@ -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";
-23
View File
@@ -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";
}
-37
View File
@@ -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";
-13
View File
@@ -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";
-27
View File
@@ -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";
-68
View File
@@ -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";
-20
View File
@@ -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";
-30
View File
@@ -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";
-9
View File
@@ -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";
-77
View File
@@ -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";
-52
View File
@@ -1,52 +0,0 @@
//test basic import/export
{
var variable: int = 42;
export variable as field;
}
{
import field as value;
assert value == 42, "import/export failed";
}
//test functions using import/export
{
fn f() {
import field;
assert field == 42, "import in function failed";
}
}
//test importing/exporting of functions
{
fn func() {
return 69;
}
export func;
}
{
import func;
assert func() == 69, "import/export of functions failed";
}
//test that variables retain their types with the typeof keyword
{
var t: type = int;
export t;
}
{
import t;
assert typeof t == type, "type retention failed";
}
print "All good";
-85
View File
@@ -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";
-43
View File
@@ -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";
-40
View File
@@ -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";
-87
View File
@@ -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();
-82
View File
@@ -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";
-50
View File
@@ -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";
-10
View File
@@ -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";
-92
View File
@@ -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";
-6
View File
@@ -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";
-339
View File
@@ -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;
-4
View File
@@ -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;
-335
View File
@@ -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;
//-------------------------
-91
View File
@@ -1,91 +0,0 @@
{
//test arrays without types
var array = [];
assert _length(array) == 0, "_length failed with array";
_push(array, 1);
_push(array, 2);
_push(array, 3);
_push(array, 4);
_push(array, "foo");
assert _length(array) == 5, "_push failed with array";
assert _pop(array) == "foo", "_pop failed with array";
_set(array, 2, "bar");
assert array == [1, 2, "bar", 4], "_set failed with array";
assert _get(array, 3) == 4, "_get failed with array";
//test dictionaries without types
var dict = [:];
_set(dict, "key", "value");
_set(dict, 1, 2);
assert dict == ["key":"value", 1:2], "_set failed with dictionaries";
assert _get(dict, "key") == "value", "_get failed with dictionaries";
//test _length
assert _length(array) == 4 && _length(dict) == 2, "_length failed with array or dictionaries";
//test clear
_clear(array);
_clear(dict);
assert _length(array) == 0 && _length(dict) == 0, "_clear failed with array or dictionaries";
}
{
//test arrays with types
var array: [int] = [];
assert _length(array) == 0, "_length failed with array (+ types)";
_push(array, 1);
_push(array, 2);
_push(array, 3);
_push(array, 4);
_push(array, 10);
assert _length(array) == 5, "_push or failed with array (+ types)";
assert _pop(array) == 10, "_pop failed with array (+ types)";
_set(array, 2, 70);
assert array == [1, 2, 70, 4], "_set failed with array (+ types)";
assert _get(array, 3) == 4, "_get failed with array (+ types)";
//test dictionaries with types
var dict: [string : string] = [:];
_set(dict, "key", "value");
assert dict == ["key":"value"], "_set failed with dictionaries (+ types)";
assert _get(dict, "key") == "value", "_get failed with dictionaries (+ types)";
//test length with types
assert _length(array) == 4 && _length(dict) == 1, "_length failed with array or dictionaries (+ types)";
//test clear with types
_clear(array);
_clear(dict);
assert _length(array) == 0 && _length(dict) == 0, "_clear failed with array or dictionaries (+ types)";
}
{
var str = "hello world";
assert _length(str) == 11, "_length failed with string";
}
print "All good";
-5
View File
@@ -1,5 +0,0 @@
//test the opaque data type works
var o: opaque = produce();
consume(o);
-8
View File
@@ -1,8 +0,0 @@
fn panic() {
assert false, "!ignore panicking within a function";
}
panic();
panic();
-9
View File
@@ -1,9 +0,0 @@
var complex: type = astype [string: [int]];
var deep: type = astype [[[ int ]]];
-11
View File
@@ -1,11 +0,0 @@
//test exports
var field: int = 42;
fn function() {
return 69;
}
export field;
export function;
print "All good";
-10
View File
@@ -1,10 +0,0 @@
//test imports
import field;
//import function;
//assert field == 42, "import field failed";
//assert function() == 69, "import function failed";
print "All good";
-25
View File
@@ -1,25 +0,0 @@
//basic types
var t: type = int;
var u: t = 42;
assert t == int, "types are not first class";
assert u == 42, "first-class types are screwing with values";
//differentiate by the "type" value
var v: type = astype [int];
var w = [int];
var x = v;
assert w == [int], "defining an array of types failed";
assert x == astype [int], "re-assigning a type value failed";
//complex type
var complex: type = astype [string : [int]];
var dict: complex = [
"first array": [1, 2, 3],
"second array": [4, 5, 6],
"third array": [7, 8, 9]
];
print "All good";
-57
View File
@@ -1,57 +0,0 @@
#include "ast_node.h"
#include "memory.h"
#include "console_colors.h"
#include <stdio.h>
int main() {
{
//test literals
char* str = "foobar";
Literal literal = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str));
ASTNode* node;
emitASTNodeLiteral(&node, literal);
freeLiteral(literal);
freeNode(node);
}
{
//test compound (dictionary)
char* idn = "foobar";
char* str = "hello world";
ASTNode* dictionary;
ASTNode* left;
ASTNode* right;
Literal identifier = TO_IDENTIFIER_LITERAL(copyString(idn, strlen(idn)), strlen(idn));
Literal string = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str));
emitASTNodeCompound(&dictionary, LITERAL_DICTIONARY);
emitASTNodeLiteral(&left, identifier);
emitASTNodeLiteral(&right, string);
//grow the node if needed
if (dictionary->compound.capacity < dictionary->compound.count + 1) {
int oldCapacity = dictionary->compound.capacity;
dictionary->compound.capacity = GROW_CAPACITY(oldCapacity);
dictionary->compound.nodes = GROW_ARRAY(ASTNode, dictionary->compound.nodes, oldCapacity, dictionary->compound.capacity);
}
//store the left and right in the node
setASTNodePair(&dictionary->compound.nodes[dictionary->compound.count++], left, right);
//the real test
freeNode(dictionary);
freeLiteral(identifier);
freeLiteral(string);
}
printf(NOTICE "All good\n" RESET);
return 0;
}
-317
View File
@@ -1,317 +0,0 @@
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "interpreter.h"
#include "console_colors.h"
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//supress the print output
static void noPrintFn(const char* output) {
//NO OP
}
//compilation functions
char* readFile(char* path, size_t* fileSize) {
FILE* file = fopen(path, "rb");
if (file == NULL) {
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
exit(-1);
}
fseek(file, 0L, SEEK_END);
*fileSize = ftell(file);
rewind(file);
char* buffer = (char*)malloc(*fileSize + 1);
if (buffer == NULL) {
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
exit(-1);
}
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
if (bytesRead < *fileSize) {
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
exit(-1);
}
fclose(file);
return buffer;
}
unsigned char* compileString(char* source, size_t* size) {
Lexer lexer;
Parser parser;
Compiler compiler;
initLexer(&lexer, source);
initParser(&parser, &lexer);
initCompiler(&compiler);
//run the parser until the end of the source
ASTNode* node = scanParser(&parser);
while(node != NULL) {
//pack up and leave
if (node->type == AST_NODEERROR) {
printf(ERROR "error node detected\n" RESET);
freeNode(node);
freeCompiler(&compiler);
freeParser(&parser);
return NULL;
}
writeCompiler(&compiler, node);
freeNode(node);
node = scanParser(&parser);
}
//get the bytecode dump
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
//cleanup
freeCompiler(&compiler);
freeParser(&parser);
//no lexer to clean up
//finally
return tb;
}
void error(char* msg) {
printf("%s", msg);
exit(-1);
}
int main() {
{
size_t size = 0;
char* source = readFile("scripts/call-from-host.toy", &size);
unsigned char* tb = compileString(source, &size);
free((void*)source);
if (!tb) {
return -1;
}
Interpreter interpreter;
initInterpreter(&interpreter);
runInterpreter(&interpreter, tb, size);
//test answer
{
interpreter.printOutput("Testing answer");
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
callFn(&interpreter, "answer", &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments has the wrong number of members\n");
}
if (returns.count != 1) {
error("Returns has the wrong number of members\n");
}
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 42) {
error("Returned value is incorrect\n");
}
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
}
//test identity
{
interpreter.printOutput("Testing identity");
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
//push an argument
float pi = 3.14;
Literal arg = TO_FLOAT_LITERAL(pi);
pushLiteralArray(&arguments, arg);
callFn(&interpreter, "identity", &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments has the wrong number of members\n");
}
if (returns.count != 1) {
error("Returns has the wrong number of members\n");
}
float epsilon = 0.1; //because floats are evil
if (!IS_FLOAT(returns.literals[0]) || fabs(AS_FLOAT(returns.literals[0]) - pi) > epsilon) {
error("Returned value is incorrect\n");
}
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
}
//test makeCounter (closures)
{
interpreter.printOutput("Testing makeCounter (closures)");
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
callFn(&interpreter, "makeCounter", &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments has the wrong number of members\n");
}
if (returns.count != 1) {
error("Returns has the wrong number of members\n");
}
//grab the resulting literal
Literal counter = popLiteralArray(&returns);
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
//call counter repeatedly
{
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
callLiteralFn(&interpreter, counter, &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments (1) has the wrong number of members\n");
}
if (returns.count != 1) {
error("Returns (1) has the wrong number of members\n");
}
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 1) {
error("Returned value (1) is incorrect\n");
}
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
}
{
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
callLiteralFn(&interpreter, counter, &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments (2) has the wrong number of members\n");
}
if (returns.count != 1) {
error("Returns (2) has the wrong number of members\n");
}
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 2) {
error("Returned value (2) is incorrect\n");
}
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
}
{
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
callLiteralFn(&interpreter, counter, &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments (3) has the wrong number of members\n");
}
if (returns.count != 1) {
error("Returns (3) has the wrong number of members\n");
}
if (!IS_INTEGER(returns.literals[0]) || AS_INTEGER(returns.literals[0]) != 3) {
error("Returned value (3) is incorrect\n");
}
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
}
freeLiteral(counter);
}
//test assertion failure
{
interpreter.printOutput("Testing assertion failure");
setInterpreterAssert(&interpreter, noPrintFn);
LiteralArray arguments;
initLiteralArray(&arguments);
LiteralArray returns;
initLiteralArray(&returns);
bool ret = callFn(&interpreter, "fail", &arguments, &returns);
//check the results
if (arguments.count != 0) {
error("Arguments has the wrong number of members\n");
}
if (returns.count != 1 || !IS_NULL(returns.literals[0])) {
error("Returns has the wrong number of members\n");
}
if (!ret) {
error("Assertion gives the wrong return value\n");
}
freeLiteralArray(&arguments);
freeLiteralArray(&returns);
}
//clean up
freeInterpreter(&interpreter);
}
printf(NOTICE "All good\n" RESET);
return 0;
}

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