Compare commits
17 Commits
8b9b012bcc
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
| 813da3e1aa | |||
| 750ebd1f99 | |||
| f55f27726c | |||
| a0d75b3c70 | |||
| 414a9d6194 | |||
| 8013ccb1da | |||
| 645fc3a457 | |||
| 2eaf0a9b0f | |||
| 3ab18c7b14 | |||
| 1660dc8b53 | |||
| b3f427d80d | |||
| 6901b9a6c9 | |||
| c9d4b9965c | |||
| ff1ef1352a | |||
| 53d3606c7e | |||
| 83fb5222a2 | |||
| 3b813da1cf |
@@ -51,3 +51,7 @@ modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
#mdbook files
|
||||
book
|
||||
mdbook
|
||||
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
# Toy v2 Quick-Start Guide
|
||||
|
||||
To help you start using Toy as fast as possible, here are the most useful elements of the language. Not everything available is listed, but this should let you start coding right away.
|
||||
|
||||
## Keyword 'print'
|
||||
|
||||
The `print` keyword takes one value as a parameter, which is sent to stdout by default, or can be redirected elsewhere using C.
|
||||
|
||||
```
|
||||
print "Hello World!";
|
||||
```
|
||||
|
||||
## Keyword 'assert'
|
||||
|
||||
The `assert` keyword takes two values as parameters, separated by a comma. If the first value is falsy or `null`, the optional second parameter is sent to stderr by default, or can be redirected elsewhere using C. If no second parameter is provided, a generic error message is used instead.
|
||||
|
||||
```
|
||||
//nothing happens
|
||||
assert 1 < 2;
|
||||
|
||||
//this assert will fail, and output the second parameter
|
||||
assert null, "Hello world!";
|
||||
```
|
||||
|
||||
## Variables and Types
|
||||
|
||||
Variables can be declared with the `var` keyword, and can be given an optional type from the list below. If no type is specified, `any` is used by default.
|
||||
|
||||
```
|
||||
var answer = 42;
|
||||
|
||||
var question: string = "How many roads must a man walk down?";
|
||||
```
|
||||
|
||||
To make a variable immutable, use the `const` keyword after the type declaration. In this case, it must be assigend a value.
|
||||
|
||||
```
|
||||
var quote: string const = "War. War never changes.";
|
||||
```
|
||||
|
||||
The types available in Toy are:
|
||||
|
||||
| type | name | description |
|
||||
| --- | --- | --- |
|
||||
| `bool` | boolean | Either `true` or `false`. |
|
||||
| `int` | integer | Any whole number (32-bits). |
|
||||
| `float` | float | A decimal number (32-bits), using floating-point arithmetic. |
|
||||
| `string` | string | A series of characters used for text processing. |
|
||||
| `array` | array | A series of values stored sequentially in memory. |
|
||||
| `table` | table | A series key-value pairs stored in such a way that allows for fast lookups. Booleans, functions, opaques and `null` can't be used as keys. |
|
||||
| `function` | function | A chunk of reusable code that takes zero or more parameters, and returns zero or more results. Functions are declared with the `fn` keyword. |
|
||||
| `opaque` | opaque | This value is unusable in Toy, but allows you to pass data between C functions. |
|
||||
| `any` | any | The default type when nothing is specified. Theis can hold any value. |
|
||||
|
||||
## Control Flow
|
||||
|
||||
Choosing an option, or repeating a chunk of code multiple times, is essential for any general purpose language.
|
||||
|
||||
Choosing between two options can be done with the `if-then-else` else statement. If the condition is truthy, the 'then-branch' will be executed. Otherwise, the optional 'else-branch' is executed instead.
|
||||
|
||||
```
|
||||
var answer = 42;
|
||||
|
||||
if (answer < 56) {
|
||||
print "Cod dang it!";
|
||||
}
|
||||
else {
|
||||
print "Something's fishy here...";
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
var challenge = "hard";
|
||||
|
||||
if (challenge == "hard") {
|
||||
print "I choose to build a scripting language, not because it's easy, but because it's hard!";
|
||||
}
|
||||
|
||||
//the else-branch is optional
|
||||
```
|
||||
|
||||
To repeat a certain action, use the `while-then` loop, which repeats the body as long as the condition is true at the beginning of each loop.
|
||||
|
||||
```
|
||||
var loops = 0;
|
||||
|
||||
while (loops++ < 8) {
|
||||
print "These episodes are endless.";
|
||||
}
|
||||
```
|
||||
|
||||
To break out of a loop, you can use the `break` keyword. Alternatively, to restart the loop early, use the `continue` keyword.
|
||||
|
||||
```
|
||||
var loops = 0;
|
||||
|
||||
while (true) {
|
||||
if (++loops < 15532) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break; //poor yuki ;_;
|
||||
}
|
||||
```
|
||||
|
||||
*Note: The `for` loop is coming, eventually, but isn't vital right now.*
|
||||
|
||||
## Arrays and Tables
|
||||
|
||||
Arrays are defined with a pair of brackets, and can contain a list of comma-separated values.
|
||||
|
||||
```
|
||||
//'array' is a reserved keyword, so it can't be used as a name
|
||||
var a = [1,2,3];
|
||||
|
||||
//instead, it's used as a type
|
||||
var b: array = [4,5,6];
|
||||
|
||||
//define an empty array like this
|
||||
var c: array = [];
|
||||
|
||||
//arrays are zero-indexed
|
||||
print a[0]; //'1'
|
||||
```
|
||||
|
||||
Tables are also defined with brackets, and contain a comma-separated list of key-value pairs defined by colons:
|
||||
|
||||
```
|
||||
//most types can be used as keys
|
||||
var t = ["alpha": 1, "beta": 2, "gamma": 3];
|
||||
|
||||
//the 'table' keyword can define the type, and an empty table still has a colon
|
||||
var u: table = [:];
|
||||
|
||||
//printing a table does NOT guarantee internal order, but the elements can be accessed with the keys.
|
||||
print t["beta"];
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
Watch this space.
|
||||
|
||||
## External Libraries and Extending Toy
|
||||
|
||||
Watch this space.
|
||||
|
||||
@@ -1,30 +1,48 @@
|
||||
<p align="center">
|
||||
<image src="toylogo.png" />
|
||||
<image src="toylogo.png" alt="The Toy Logo" />
|
||||
</p>
|
||||
|
||||
**This is a work in progress, and is not yet fit for purpose. I hope I can get it to a useable state, but personal issues can often make dedicating myself to a project difficult. Your patience and support is greatly appreciated.**
|
||||
|
||||
# Toy v2.x
|
||||
|
||||
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 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 external scripts.
|
||||
|
||||
This repository holds the reference implementation for Toy version 2.x, written in C.
|
||||
This repository holds the reference implementation for Toy version 2.x, written in C - alpha testing is currently underway.
|
||||
|
||||
# Nifty Features
|
||||
|
||||
* Simple C-style syntax
|
||||
* Simple C-like syntax
|
||||
* Intermediate AST and bytecode representations
|
||||
* Strong, but optional type system
|
||||
* First-class functions and closures
|
||||
* Extensible with imported native code
|
||||
* Can re-direct output, error and assert failure messages
|
||||
* Extensible with native C-bindings
|
||||
* Can re-direct output, error and assertion messages
|
||||
* Open-Source under the zlib license
|
||||
|
||||
# Syntax
|
||||
|
||||
Watch this space.
|
||||
```toy
|
||||
fn makeCounter() {
|
||||
var counter: Int = 0;
|
||||
|
||||
(The `scripts` or `tests` directory might help, the docs website is WIP.)
|
||||
fn increment() {
|
||||
return ++counter;
|
||||
}
|
||||
|
||||
return increment;
|
||||
}
|
||||
|
||||
var tally = makeCounter();
|
||||
|
||||
while (true) {
|
||||
var result = tally();
|
||||
|
||||
print result; //prints 1 to 10
|
||||
|
||||
if (result >= 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Building
|
||||
|
||||
@@ -32,15 +50,9 @@ This project requires `gcc` and `make` by default, but should also work in other
|
||||
|
||||
Run `make` in the root directory to build the shared library named `libToy.so` and a useable REPL named `repl.out`.
|
||||
|
||||
# Tools
|
||||
|
||||
Watch this space.
|
||||
|
||||
(There's some utility functions in `repl/` that are WIP but useful.)
|
||||
|
||||
# Documentation
|
||||
|
||||
Watch this space.
|
||||
The contents of `docs/` is also available on the official website [toylang.com](https://toylang.com/).
|
||||
|
||||
# License
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[book]
|
||||
title = "The Toy Programming Language"
|
||||
authors = ["Kayne Ruse (Ratstail91)"]
|
||||
description = "Documentation For The Toy Programming Language"
|
||||
language = "en"
|
||||
|
||||
[output.html]
|
||||
git-repository-url = "https://github.com/krgamestudios/Toy"
|
||||
git-repository-icon = "fab-github"
|
||||
@@ -0,0 +1,3 @@
|
||||
# 404
|
||||
|
||||
Nobody here but us chickens!
|
||||
@@ -0,0 +1,47 @@
|
||||
<p align="center">
|
||||
<image src="img/toylogo.png" alt="The Toy Logo" />
|
||||
</p>
|
||||
|
||||
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 external scripts.
|
||||
|
||||
## Nifty Features
|
||||
|
||||
* Simple C-like syntax
|
||||
* Intermediate AST and bytecode representations
|
||||
* Strong, but optional type system
|
||||
* First-class functions and closures
|
||||
* Extensible with native C-bindings
|
||||
* Can re-direct output, error and assertion messages
|
||||
* Open-Source under the zlib license
|
||||
|
||||
## Syntax
|
||||
|
||||
```toy
|
||||
fn makeCounter() {
|
||||
var counter: Int = 0;
|
||||
|
||||
fn increment() {
|
||||
return ++counter;
|
||||
}
|
||||
|
||||
return increment;
|
||||
}
|
||||
|
||||
var tally = makeCounter();
|
||||
|
||||
while (true) {
|
||||
var result = tally();
|
||||
|
||||
print result; //prints 1 to 10
|
||||
|
||||
if (result >= 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
This website is a work in progress - for further info, see the official 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) (a simple "game" used for testing).
|
||||
@@ -0,0 +1,5 @@
|
||||
# Summary
|
||||
|
||||
- [Front Page](./README.md)
|
||||
- [Quick Start](./quickstart.md)
|
||||
- [Cheat Sheet](./cheatsheet.md)
|
||||
@@ -0,0 +1,144 @@
|
||||
# Cheat Sheet
|
||||
|
||||
## Compile and Run A Snippet Of Code
|
||||
|
||||
```c
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_compiler.h"
|
||||
#include "toy_vm.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
//example code
|
||||
const char* source = "print \"Hello world!\";";
|
||||
|
||||
//buckets use the arena pattern for memory allocation
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
|
||||
//compile the code
|
||||
Toy_Lexer lexer;
|
||||
Toy_bindLexer(&lexer, (char*)source);
|
||||
|
||||
Toy_Parser parser;
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||
|
||||
//the ast, which is stored in this bucket, is no longer needed
|
||||
Toy_freeBucket(&bucket);
|
||||
|
||||
//the virtual machine used at runtime
|
||||
Toy_VM vm;
|
||||
Toy_initVM(&vm);
|
||||
Toy_bindVM(&vm, bytecode, NULL);
|
||||
|
||||
//execute the given code
|
||||
Toy_runVM(&vm);
|
||||
|
||||
//cleanup after ourselves
|
||||
Toy_freeVM(&vm);
|
||||
free(bytecode);
|
||||
}
|
||||
```
|
||||
|
||||
## Quick and Dirty Compilation
|
||||
|
||||
```c
|
||||
unsigned char* compileSource(const char* source) {
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
|
||||
Toy_Lexer lexer;
|
||||
Toy_bindLexer(&lexer, source);
|
||||
|
||||
Toy_Parser parser;
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||
|
||||
Toy_freeBucket(&bucket);
|
||||
return bytecode;
|
||||
}
|
||||
```
|
||||
|
||||
## API Functions
|
||||
|
||||
This is a rough outline of all API functions declared in Toy's headers. As a rule, functions that begin with `TOY_API` are useable and begin with `Toy_`, while functions that begin with `Toy_private_` are generally not intended for use, and only exposed for technical reasons.
|
||||
|
||||
*Note: This list is updated manually, if something is outdated let me know.*
|
||||
|
||||
```c
|
||||
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);
|
||||
TOY_API void Toy_setOpaqueAttributeHandler(Toy_OpaqueAttributeHandler cb);
|
||||
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
|
||||
TOY_API Toy_Bucket* Toy_allocateBucket(unsigned int capacity);
|
||||
TOY_API unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount);
|
||||
TOY_API void Toy_releaseBucketPartition(unsigned char* ptr);
|
||||
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);
|
||||
TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
|
||||
TOY_API unsigned char* Toy_compileToBytecode(Toy_Ast* ast);
|
||||
TOY_API void Toy_freeFunction(Toy_Function* fn) {
|
||||
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope);
|
||||
TOY_API Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nativeCallback callback);
|
||||
TOY_API Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* fn);
|
||||
TOY_API void Toy_freeFunction(Toy_Function* fn);
|
||||
TOY_API void Toy_bindLexer(Toy_Lexer* lexer, const char* source);
|
||||
TOY_API void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer);
|
||||
TOY_API Toy_Ast* Toy_scanParser(Toy_Bucket** bucketHandle, Toy_Parser* parser);
|
||||
TOY_API void Toy_resetParser(Toy_Parser* parser);
|
||||
TOY_API void Toy_print(const char* msg);
|
||||
TOY_API void Toy_error(const char* msg);
|
||||
TOY_API void Toy_assertFailure(const char* msg);
|
||||
TOY_API void Toy_setPrintCallback(Toy_callbackType cb);
|
||||
TOY_API void Toy_setErrorCallback(Toy_callbackType cb);
|
||||
TOY_API void Toy_setAssertFailureCallback(Toy_callbackType cb);
|
||||
TOY_API void Toy_resetPrintCallback(void);
|
||||
TOY_API void Toy_resetErrorCallback(void);
|
||||
TOY_API void Toy_resetAssertFailureCallback(void);
|
||||
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
|
||||
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant);
|
||||
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
|
||||
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
|
||||
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
||||
TOY_API Toy_Stack* Toy_allocateStack(void);
|
||||
TOY_API void Toy_freeStack(Toy_Stack* stack);
|
||||
TOY_API void Toy_resetStack(Toy_Stack** stackHandle);
|
||||
TOY_API void Toy_pushStack(Toy_Stack** stackHandle, Toy_Value value);
|
||||
TOY_API Toy_Value Toy_peekStack(Toy_Stack** stackHandle);
|
||||
TOY_API Toy_Value Toy_popStack(Toy_Stack** stackHandle);
|
||||
TOY_API Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring);
|
||||
TOY_API Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
|
||||
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
|
||||
TOY_API Toy_String* Toy_copyString(Toy_String* str);
|
||||
TOY_API Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right);
|
||||
TOY_API void Toy_freeString(Toy_String* str);
|
||||
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
|
||||
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
|
||||
TOY_API char* Toy_getStringRaw(Toy_String* str);
|
||||
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right);
|
||||
TOY_API unsigned int Toy_hashString(Toy_String* string);
|
||||
TOY_API Toy_Table* Toy_allocateTable(unsigned int minCapacity);
|
||||
TOY_API void Toy_freeTable(Toy_Table* table);
|
||||
TOY_API void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value);
|
||||
TOY_API Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key);
|
||||
TOY_API void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key);
|
||||
TOY_API Toy_Value Toy_unwrapValue(Toy_Value value);
|
||||
TOY_API unsigned int Toy_hashValue(Toy_Value value);
|
||||
TOY_API Toy_Value Toy_copyValue(struct Toy_Bucket** bucketHandle, Toy_Value value);
|
||||
TOY_API void Toy_freeValue(Toy_Value value);
|
||||
TOY_API bool Toy_checkValueIsTruthy(Toy_Value value);
|
||||
TOY_API bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right);
|
||||
TOY_API bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right);
|
||||
TOY_API int Toy_compareValues(Toy_Value left, Toy_Value right);
|
||||
TOY_API union Toy_String_t* Toy_stringifyValue(struct Toy_Bucket** bucketHandle, Toy_Value value);
|
||||
TOY_API const char* Toy_getValueTypeAsCString(Toy_ValueType type);
|
||||
TOY_API void Toy_resetVM(Toy_VM* vm, bool preserveScope, bool preserveStack);
|
||||
TOY_API void Toy_initVM(Toy_VM* vm);
|
||||
TOY_API void Toy_inheritVM(Toy_VM* parentVM, Toy_VM* subVM);
|
||||
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
|
||||
TOY_API void Toy_freeVM(Toy_VM* vm);
|
||||
TOY_API Toy_Value Toy_getReturnValueFromVM(Toy_VM* parentVM, Toy_VM* subVM);
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 454 KiB |
@@ -0,0 +1,186 @@
|
||||
# Toy v2 Quick-Start Guide
|
||||
|
||||
To help you start using Toy as fast as possible, here are the most useful elements of the language. Not everything available is listed, but this should let you start coding right away.
|
||||
|
||||
## Keyword 'print'
|
||||
|
||||
The `print` keyword prints a given value to stdout (or elsewhere if configured with the API).
|
||||
|
||||
```
|
||||
print "Hello World!";
|
||||
```
|
||||
|
||||
## Keyword 'assert'
|
||||
|
||||
The `assert` keyword takes two values separated by a comma. If the first value is falsy or `null` the optional second parameter is printed to stderr (or elsewhere if configured with the API). If no second parameter is provided a generic error message is used instead.
|
||||
|
||||
```
|
||||
//nothing happens
|
||||
assert 1 < 2;
|
||||
|
||||
//this assert will fail, and output the second parameter
|
||||
assert null, "Hello world!";
|
||||
```
|
||||
|
||||
## Variables and Types
|
||||
|
||||
Variables are declared with the `var` keyword with and an optional type from the list below. If no type is specified `Any` is used instead.
|
||||
|
||||
```
|
||||
var answer = 42;
|
||||
|
||||
var question: String = "How many roads must a man walk down?";
|
||||
```
|
||||
|
||||
To make a variable immutable put the `const` keyword after the type. If you do, it must be assigned a value.
|
||||
|
||||
```
|
||||
var quote: String const = "War. War never changes.";
|
||||
```
|
||||
|
||||
Toy's types are:
|
||||
|
||||
| type | name | description |
|
||||
| --- | --- | --- |
|
||||
| `Bool` | Boolean | Either `true` or `false`. |
|
||||
| `Int` | Integer | Any signed whole number (32-bits). |
|
||||
| `Float` | Float | Any signed decimal number (32-bits), using floating point arithmatic. |
|
||||
| `String` | String | Normal text, effectively utf-8. |
|
||||
| `Array` | Array | A series of values stored sequentially in memory. |
|
||||
| `Table` | Table | A series key-value pairs stored in a hash table. Booleans, functions, opaques and `null` can't be used as keys. |
|
||||
| `Function` | Function | A chunk of reusable code that takes zero or more parameters, and may return a result. Functions are declared with the `fn` keyword, or in the API. |
|
||||
| `Opaque` | Opaque | This value is unusable in Toy, but allows you to pass data between C bindings provided with the API. |
|
||||
| `Any` | Any | The default type when nothing is specified. It can hold any value. |
|
||||
|
||||
## Control Flow
|
||||
|
||||
Making a decision, or repeating a chunk of code multiple times, is essential for any language. Choosing between multiple options can be done with the `if-then-else` statement - if the condition is truthy, the 'then-branch' will be executed. Otherwise, the optional 'else-branch' is executed instead.
|
||||
|
||||
```
|
||||
var answer = 42;
|
||||
|
||||
if (answer < 56) {
|
||||
print "Cod dang it!";
|
||||
}
|
||||
else {
|
||||
print "Something's fishy here...";
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
var challenge = "hard";
|
||||
|
||||
if (challenge == "hard") {
|
||||
print "I choose to build a scripting language, not because it's easy, but because it's hard!";
|
||||
}
|
||||
|
||||
//the else-branch is optional
|
||||
```
|
||||
|
||||
To repeat a certain action, use the `while-then` loop, which repeats the body as long as the given condition remains true on each loop.
|
||||
|
||||
```
|
||||
var loops = 0;
|
||||
|
||||
while (loops++ < 8) {
|
||||
print "These episodes are endless.";
|
||||
}
|
||||
```
|
||||
|
||||
To break out of a loop, you can use the `break` keyword. Alternatively, to restart the loop early, use the `continue` keyword.
|
||||
|
||||
```
|
||||
var loops = 0;
|
||||
|
||||
while (true) {
|
||||
if (++loops < 15532) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break; //poor yuki ;_;
|
||||
}
|
||||
```
|
||||
|
||||
*Note: The `for` loop is coming soon, and will allow for iteration over an array or table, but isn't vital right now.*
|
||||
|
||||
## Arrays and Tables
|
||||
|
||||
Arrays are defined with a pair of brackets, and can contain a list of comma-separated values.
|
||||
|
||||
```
|
||||
//define an array
|
||||
var array = [1,2,3];
|
||||
|
||||
//specify the type
|
||||
var bray: Array = [4,5,6];
|
||||
|
||||
//define an empty array
|
||||
var craycray: Array = [];
|
||||
|
||||
//arrays are zero-indexed
|
||||
print array[0]; //'1'
|
||||
```
|
||||
|
||||
Tables are also defined with brackets, and contain a comma-separated list of key-value pairs defined by colons:
|
||||
|
||||
```
|
||||
//most types can be used as keys
|
||||
var table = ["alpha": 1, "beta": 2, "gamma": 3];
|
||||
|
||||
//the 'Table' keyword can define the type, and an empty table still has a colon
|
||||
var under: Table = [:];
|
||||
|
||||
//printing the whole table does NOT guarantee internal order
|
||||
print table["beta"];
|
||||
```
|
||||
|
||||
## Attributes
|
||||
|
||||
Some values, including Strings, Arrays and Tables, have "attributes" which are accessible with the dot `.` operator. These can expose internal values or components for manipulating said values.
|
||||
|
||||
```
|
||||
var string = "Hello World";
|
||||
print string.length; //11
|
||||
print string.asUpper; //HELLO WORLD
|
||||
print string.asLower; //hello world
|
||||
|
||||
var array = [1,2,3];
|
||||
array.pushBack(4); //array = [1,2,3,4]
|
||||
var element = array.popBack(); //element = 4
|
||||
var emptyArray = [];
|
||||
|
||||
var table = ["alpha": 1, "beta":2];
|
||||
print table.length; //2
|
||||
table.insert("key",element); //table["key"] = 4
|
||||
print table.hasKey("alpha"); //true
|
||||
table.remove("alpha"); //table = ["beta":2,"key":4]
|
||||
var emptyTable = [:];
|
||||
```
|
||||
|
||||
Opaques can also be given attributes, but this requires some in-depth understanding of the API, so won't be covered here.
|
||||
|
||||
## Functions
|
||||
|
||||
Functions are defined with the `fn` keyword, and follow a c-like syntax, with optional types on each parameter:
|
||||
|
||||
```toy
|
||||
fn fib(n: Int) {
|
||||
if (n < 2) return n;
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
print fib(12); //144
|
||||
```
|
||||
|
||||
```toy
|
||||
fn isLeapYear(n: Int) {
|
||||
if (n % 400 == 0) return true;
|
||||
if (n % 100 == 0) return false;
|
||||
return n % 4 == 0;
|
||||
}
|
||||
```
|
||||
|
||||
## External API and Extending Toy
|
||||
|
||||
*Note: Watch this space, docs for the C API are coming soon. For now, the [Cheat Sheet](/cheatsheet) can get you started.*
|
||||
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
<!-- open graph protocol -->
|
||||
<meta property="og:url" content="{{ url }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="{{ base_url }}/img/toypreview.png" />
|
||||
<meta property="og:title" content="{{ title }}" />
|
||||
<meta property="og:description" content="{{ description }}" />
|
||||
|
||||
<!-- twitter has to be special -->
|
||||
<meta name="twitter:card" content="{{ title }}" />
|
||||
<meta name="twitter:url" content="{{ url}}" />
|
||||
<meta name="twitter:type" content="website" />
|
||||
<meta name="twitter:image" content="{{ base_url }}/img/toypreview.png" />
|
||||
<meta name="twitter:title" content="{{ title }}" />
|
||||
<meta name="twitter:description" content="{{ description }}" />
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "bucket_inspector.h"
|
||||
#include <toy_string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int inspect_bucket(Toy_Bucket** bucketHandle) {
|
||||
int depth = 0;
|
||||
|
||||
//for each bucket
|
||||
for (Toy_Bucket* iter = (*bucketHandle); iter != NULL; iter = iter->next) {
|
||||
int occupied = 0;
|
||||
int released = 0;
|
||||
unsigned char* ptr = iter->data;
|
||||
|
||||
|
||||
while ((ptr - iter->data < iter->count) && *((int*)ptr) != 0) { //for each partition
|
||||
if ( ( *((int*)ptr) & 1) == 0) { //is this partition still in use?
|
||||
occupied++;
|
||||
|
||||
//try to print as a string if possible
|
||||
Toy_String* str = (void*)(ptr + 4);
|
||||
|
||||
if (str->info.type == TOY_STRING_LEAF && str->info.length < 255) {
|
||||
printf("String Leaf (%d bytes, %d refCount): %.*s\n", *((int*)ptr), str->info.refCount, str->info.length, str->leaf.data);
|
||||
}
|
||||
else if (str->info.type == TOY_STRING_NODE) {
|
||||
printf("String Node (%d bytes, %d refCount): ...\n", *((int*)ptr), str->info.refCount);
|
||||
}
|
||||
}
|
||||
else {
|
||||
released++;
|
||||
}
|
||||
|
||||
//jump distance: ((*((int*)ptr) | 1) ^ 1) + 4
|
||||
// printf(" jump %d, ", ((*((int*)ptr) | 1) ^ 1) + 4);
|
||||
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||
}
|
||||
|
||||
printf("Bucket link %d: count %u, %d occupied, %d released\n", depth, iter->count, occupied, released);
|
||||
|
||||
depth++;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return depth;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "toy_bucket.h"
|
||||
|
||||
int inspect_bucket(Toy_Bucket** bucketHandle);
|
||||
@@ -385,5 +385,3 @@ int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_ad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Check if strings are reused in the bytecode
|
||||
+36
-8
@@ -1,5 +1,6 @@
|
||||
#include "ast_inspector.h"
|
||||
#include "bytecode_inspector.h"
|
||||
#include "bucket_inspector.h"
|
||||
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
@@ -279,14 +280,14 @@ static void debugScopePrint(Toy_Scope* scope, int depth) {
|
||||
|
||||
printf("\n" TOY_CC_NOTICE "Scope Dump [%d]" TOY_CC_RESET "\n" TOY_CC_NOTICE "%-20s%-20s%-20s" TOY_CC_RESET "\n", depth, "type", "name", "value");
|
||||
for (unsigned int i = 0; i < scope->capacity; i++) {
|
||||
if (scope->data[i].key.info.length == 0) {
|
||||
if (scope->data[i].key == NULL || scope->data[i].key->info.length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Toy_String k = scope->data[i].key;
|
||||
Toy_String* k = scope->data[i].key;
|
||||
Toy_Value v = scope->data[i].value;
|
||||
|
||||
printf("%-10s%-10s%-20s", Toy_getValueTypeAsCString(scope->data[i].type), scope->data[i].constant ? "const" : "", k.leaf.data);
|
||||
printf("%-10s%-10s%-20s", Toy_getValueTypeAsCString(scope->data[i].type), scope->data[i].constant ? "const" : "", k != NULL ? k->leaf.data : "");
|
||||
|
||||
//print value
|
||||
Toy_String* string = Toy_stringifyValue(&stringBucket, Toy_unwrapValue(v));
|
||||
@@ -320,8 +321,6 @@ int repl(const char* filepath, bool verbose) {
|
||||
char inputBuffer[INPUT_BUFFER_SIZE];
|
||||
memset(inputBuffer, 0, INPUT_BUFFER_SIZE);
|
||||
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
|
||||
Toy_VM vm;
|
||||
Toy_initVM(&vm);
|
||||
|
||||
@@ -346,14 +345,16 @@ int repl(const char* filepath, bool verbose) {
|
||||
}
|
||||
|
||||
//parse the input, prep the VM for execution
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
Toy_Lexer lexer;
|
||||
Toy_bindLexer(&lexer, inputBuffer);
|
||||
Toy_Parser parser;
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser); //Ast is in the bucket, so it doesn't need to be freed
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//parsing error, retry
|
||||
if (parser.error) {
|
||||
if (parser.error || ast == NULL) {
|
||||
Toy_freeBucket(&bucket);
|
||||
printf("%s> ", prompt); //shows the terminal prompt
|
||||
continue;
|
||||
}
|
||||
@@ -363,6 +364,12 @@ int repl(const char* filepath, bool verbose) {
|
||||
}
|
||||
|
||||
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||
Toy_freeBucket(&bucket); //no need to for the GC here
|
||||
|
||||
if (bytecode == NULL) {
|
||||
printf("%s> ", prompt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
inspect_bytecode(bytecode);
|
||||
@@ -380,14 +387,26 @@ int repl(const char* filepath, bool verbose) {
|
||||
//run
|
||||
Toy_runVM(&vm);
|
||||
|
||||
int depthBeforeGC = 0;
|
||||
int depthAfterGC = 0;
|
||||
|
||||
//print the debug info
|
||||
if (verbose) {
|
||||
debugStackPrint(vm.stack);
|
||||
debugScopePrint(vm.scope, 0);
|
||||
|
||||
depthBeforeGC = inspect_bucket(&vm.memoryBucket);
|
||||
}
|
||||
|
||||
//free the memory, and leave the VM ready for the next loop
|
||||
Toy_resetVM(&vm, true, true);
|
||||
|
||||
if (verbose) {
|
||||
depthAfterGC = inspect_bucket(&vm.memoryBucket);
|
||||
|
||||
printf("GC Report: %d -> %d\n", depthBeforeGC, depthAfterGC);
|
||||
}
|
||||
|
||||
free(bytecode);
|
||||
|
||||
printf("%s> ", prompt); //shows the terminal prompt
|
||||
@@ -395,7 +414,6 @@ int repl(const char* filepath, bool verbose) {
|
||||
|
||||
//cleanup all memory
|
||||
Toy_freeVM(&vm);
|
||||
Toy_freeBucket(&bucket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -466,6 +484,12 @@ int main(int argc, const char* argv[]) {
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
if (ast == NULL) {
|
||||
Toy_freeBucket(&bucket);
|
||||
free(source);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmd.verbose) {
|
||||
inspect_ast(ast);
|
||||
}
|
||||
@@ -474,6 +498,10 @@ int main(int argc, const char* argv[]) {
|
||||
Toy_freeBucket(&bucket);
|
||||
free(source);
|
||||
|
||||
if (bytecode == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmd.verbose) {
|
||||
inspect_bytecode(bytecode);
|
||||
}
|
||||
|
||||
+51
-13
@@ -15,18 +15,21 @@ typedef struct CallbackPairs {
|
||||
} CallbackPairs;
|
||||
|
||||
//example callbacks
|
||||
void debug(Toy_VM* vm) {
|
||||
static void answer(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)vm;
|
||||
Toy_print("This function returns the integer '42' to the calling scope.");
|
||||
(void)self;
|
||||
Toy_print(TOY_CC_DEBUG "This function returns the integer '42' to the calling scope." TOY_CC_RESET);
|
||||
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(42));
|
||||
}
|
||||
|
||||
void identity(Toy_VM* vm) {
|
||||
static void identity(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
//does nothing, but any arguements are left on the stack as results
|
||||
(void)vm;
|
||||
(void)self;
|
||||
}
|
||||
|
||||
void echo(Toy_VM* vm) {
|
||||
static void echo(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
//pops one argument, and prints it
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value);
|
||||
@@ -39,10 +42,49 @@ void echo(Toy_VM* vm) {
|
||||
Toy_freeValue(value);
|
||||
}
|
||||
|
||||
static void next(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
//used by 'range'
|
||||
if (self->meta2 < self->meta1) {
|
||||
Toy_Value result = TOY_VALUE_FROM_INTEGER(self->meta2);
|
||||
Toy_pushStack(&vm->stack, result);
|
||||
self->meta2++;
|
||||
}
|
||||
else {
|
||||
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
|
||||
}
|
||||
}
|
||||
|
||||
static void range(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
|
||||
//one arg to represent the number of iterations
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
|
||||
//check types
|
||||
if (!TOY_VALUE_IS_INTEGER(value)) {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "Expected Integer argument in 'range', found '%s'", Toy_getValueTypeAsCString(value.type));
|
||||
Toy_error(buffer);
|
||||
Toy_freeValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
//make the callback
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, next);
|
||||
fn->native.meta1 = TOY_VALUE_AS_INTEGER(value); //fake a closure
|
||||
fn->native.meta2 = 0; //counter
|
||||
|
||||
Toy_Value result = TOY_VALUE_FROM_FUNCTION(fn);
|
||||
|
||||
Toy_pushStack(&vm->stack, result);
|
||||
}
|
||||
|
||||
|
||||
CallbackPairs callbackPairs[] = {
|
||||
{"debug", debug},
|
||||
{"identity", identity},
|
||||
{"echo", echo},
|
||||
{"dbg_answer", answer},
|
||||
{"dbg_identity", identity},
|
||||
{"dbg_echo", echo},
|
||||
{"range", range},
|
||||
|
||||
{NULL, NULL},
|
||||
};
|
||||
@@ -56,13 +98,9 @@ void initStandardLibrary(Toy_VM* vm) {
|
||||
|
||||
//declare each pair
|
||||
for (int i = 0; callbackPairs[i].name; i++) {
|
||||
//cheat
|
||||
Toy_String key = (Toy_String){
|
||||
.leaf = { ._padding = { .type = TOY_STRING_LEAF, .length = strlen(callbackPairs[i].name), .refCount = 1, .cachedHash = 0 }, .data = callbackPairs[i].name }
|
||||
};
|
||||
|
||||
Toy_String* key = Toy_createStringLength(&vm->memoryBucket, callbackPairs[i].name, strlen(callbackPairs[i].name));
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&(vm->memoryBucket), callbackPairs[i].callback);
|
||||
|
||||
Toy_declareScope(vm->scope, &key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true);
|
||||
Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//WARN: This is just a scratch pad, don't use it
|
||||
//TODO: table.hasValue or table.getKeyFromValue?
|
||||
|
||||
|
||||
//for (var i in array) print i;
|
||||
//for (var i in table) print i;
|
||||
//for (var i in range(10)) print i;
|
||||
//for (range(10)) print "ha";
|
||||
|
||||
|
||||
//example of a `range`-like function
|
||||
fn range(limit: Int) {
|
||||
var counter: Int = 0;
|
||||
|
||||
fn next() {
|
||||
if (counter >= limit) {
|
||||
return null;
|
||||
}
|
||||
else return counter++;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
var next = range(10);
|
||||
|
||||
|
||||
fn log(x) {
|
||||
if (x == null) return;
|
||||
print x;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
log(next());
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
|
||||
|
||||
fn swap(a, b) {
|
||||
return b, a;
|
||||
|
||||
fn a(x) {
|
||||
print x;
|
||||
}
|
||||
|
||||
var a = 42;
|
||||
var b = 69;
|
||||
var c;
|
||||
var d;
|
||||
fn b() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
//BUG: still causes a segfault
|
||||
c, d = swap(a, b);
|
||||
a(b(), b());
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
|
||||
var randi: Int = 69420;
|
||||
fn rand() {
|
||||
return randi = randi * 1664525 + 1013904223;
|
||||
}
|
||||
|
||||
|
||||
var a = rand();
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) {
|
||||
|
||||
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity;
|
||||
|
||||
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array)); //URGENT: Swap to a bucket
|
||||
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array));
|
||||
|
||||
if (array == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize a 'Toy_Array' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity);
|
||||
|
||||
+23
-1
@@ -16,7 +16,7 @@ void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_A
|
||||
//first, check if we're an empty head
|
||||
if (block->block.child == NULL) {
|
||||
block->block.child = child;
|
||||
return; //NOTE: first call on an empty head skips any memory allocations
|
||||
return; //First call on an empty head skips any memory allocations
|
||||
}
|
||||
|
||||
//run (or jump) until we hit the current tail
|
||||
@@ -147,6 +147,16 @@ void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
tmp->type = TOY_AST_FOR_THEN;
|
||||
tmp->forThen.condBranch = condBranch;
|
||||
tmp->forThen.thenBranch = thenBranch;
|
||||
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
@@ -244,6 +254,16 @@ void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
tmp->type = TOY_AST_ITERABLE;
|
||||
tmp->iterable.left = (*astHandle);
|
||||
tmp->iterable.right = expr;
|
||||
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
@@ -293,6 +313,7 @@ const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
|
||||
case TOY_AST_ASSERT: return "ASSERT";
|
||||
case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE";
|
||||
case TOY_AST_WHILE_THEN: return "WHILE_THEN";
|
||||
case TOY_AST_FOR_THEN: return "FOR_THEN";
|
||||
case TOY_AST_BREAK: return "BREAK";
|
||||
case TOY_AST_CONTINUE: return "CONTINUE";
|
||||
case TOY_AST_RETURN: return "RETURN";
|
||||
@@ -305,6 +326,7 @@ const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
|
||||
case TOY_AST_FN_DECLARE: return "FN_DECLARE";
|
||||
case TOY_AST_FN_INVOKE: return "FN_INVOKE";
|
||||
case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
|
||||
case TOY_AST_ITERABLE: return "ITERABLE";
|
||||
|
||||
case TOY_AST_STACK_POP: return "STACK_POP";
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_ASSERT,
|
||||
TOY_AST_IF_THEN_ELSE,
|
||||
TOY_AST_WHILE_THEN,
|
||||
TOY_AST_FOR_THEN,
|
||||
TOY_AST_BREAK,
|
||||
TOY_AST_CONTINUE,
|
||||
TOY_AST_RETURN,
|
||||
@@ -34,6 +35,7 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_FN_DECLARE,
|
||||
TOY_AST_FN_INVOKE,
|
||||
TOY_AST_ATTRIBUTE,
|
||||
TOY_AST_ITERABLE,
|
||||
|
||||
TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements
|
||||
|
||||
@@ -76,6 +78,7 @@ typedef enum Toy_AstFlag {
|
||||
TOY_AST_FLAG_COLLECTION = 32,
|
||||
TOY_AST_FLAG_PAIR = 33,
|
||||
TOY_AST_FLAG_INDEX = 34,
|
||||
TOY_AST_FLAG_FN_ARGUMENTS = 35,
|
||||
|
||||
//unary flags
|
||||
TOY_AST_FLAG_NEGATE = 40,
|
||||
@@ -170,6 +173,12 @@ typedef struct Toy_AstWhileThen {
|
||||
Toy_Ast* thenBranch;
|
||||
} Toy_AstWhileThen;
|
||||
|
||||
typedef struct Toy_AstForThen {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* condBranch;
|
||||
Toy_Ast* thenBranch;
|
||||
} Toy_AstForThen;
|
||||
|
||||
typedef struct Toy_AstBreak {
|
||||
Toy_AstType type;
|
||||
} Toy_AstBreak;
|
||||
@@ -227,6 +236,12 @@ typedef struct Toy_AstAttribute {
|
||||
Toy_Ast* right;
|
||||
} Toy_AstAttribute;
|
||||
|
||||
typedef struct Toy_AstIterable {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* left;
|
||||
Toy_Ast* right;
|
||||
} Toy_AstIterable;
|
||||
|
||||
typedef struct Toy_AstStackPop {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* child;
|
||||
@@ -258,6 +273,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
|
||||
Toy_AstAssert assert;
|
||||
Toy_AstIfThenElse ifThenElse;
|
||||
Toy_AstWhileThen whileThen;
|
||||
Toy_AstForThen forThen;
|
||||
Toy_AstBreak breakPoint;
|
||||
Toy_AstContinue continuePoint;
|
||||
Toy_AstReturn fnReturn;
|
||||
@@ -268,6 +284,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
|
||||
Toy_AstFnDeclare fnDeclare;
|
||||
Toy_AstFnInvoke fnInvoke;
|
||||
Toy_AstAttribute attribute;
|
||||
Toy_AstIterable iterable;
|
||||
Toy_AstStackPop stackPop;
|
||||
Toy_AstPass pass;
|
||||
Toy_AstError error;
|
||||
@@ -289,6 +306,7 @@ void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
|
||||
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
|
||||
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);
|
||||
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
|
||||
void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
|
||||
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
@@ -301,6 +319,7 @@ void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astH
|
||||
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body);
|
||||
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params);
|
||||
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
|
||||
void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
|
||||
|
||||
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
|
||||
+47
-25
@@ -9,13 +9,17 @@
|
||||
//if set, used for delegating to user-defined code
|
||||
static Toy_OpaqueAttributeHandler opaqueAttributeCallback = NULL;
|
||||
|
||||
//NOTE: there is no need to call 'Toy_freeValue' on the arguments, as the VM assumes you don't
|
||||
//utils
|
||||
#define MATCH_VALUE_AND_CSTRING(value, cstring) \
|
||||
((TOY_VALUE_AS_STRING(value)->info.length == strlen(cstring)) && \
|
||||
(strncmp(cstring, TOY_VALUE_AS_STRING(value)->leaf.data, TOY_VALUE_AS_STRING(value)->info.length) == 0))
|
||||
|
||||
//NOTE: there is no need to call 'Toy_freeValue' on the arguments, as the VM assumes you don't
|
||||
Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||
if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) {
|
||||
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_STRING(compound)->info.length);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "asUpper", 7) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "asUpper")) {
|
||||
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
|
||||
for (int i = 0; buffer[i] != '\0'; i++) {
|
||||
buffer[i] = toupper(buffer[i]);
|
||||
@@ -24,7 +28,7 @@ Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy
|
||||
free(buffer);
|
||||
return TOY_VALUE_FROM_STRING(str);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "asLower", 7) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "asLower")) {
|
||||
char* buffer = Toy_getStringRaw(TOY_VALUE_AS_STRING(compound));
|
||||
for (int i = 0; buffer[i] != '\0'; i++) {
|
||||
buffer[i] = tolower(buffer[i]);
|
||||
@@ -41,7 +45,9 @@ Toy_Value Toy_private_handleStringAttributes(Toy_VM* vm, Toy_Value compound, Toy
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_arrayPushBack(Toy_VM* vm) {
|
||||
static void attr_arrayPushBack(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
|
||||
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||
Toy_Value element = Toy_popStack(&vm->stack);
|
||||
|
||||
@@ -65,7 +71,9 @@ static void attr_arrayPushBack(Toy_VM* vm) {
|
||||
array->count++;
|
||||
}
|
||||
|
||||
static void attr_arrayPopBack(Toy_VM* vm) {
|
||||
static void attr_arrayPopBack(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
|
||||
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||
|
||||
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
|
||||
@@ -82,7 +90,10 @@ static void attr_arrayPopBack(Toy_VM* vm) {
|
||||
Toy_pushStack(&vm->stack, element);
|
||||
}
|
||||
|
||||
static void attr_arrayForEach(Toy_VM* vm) {
|
||||
static void attr_arrayForEach(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
//URGENT: replace with for-loop
|
||||
(void)self;
|
||||
|
||||
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||
Toy_Value callback = Toy_popStack(&vm->stack);
|
||||
|
||||
@@ -116,9 +127,10 @@ static void attr_arrayForEach(Toy_VM* vm) {
|
||||
Toy_declareScope(subVM.scope, Toy_copyString(name), paramType, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]), true);
|
||||
Toy_freeString(name);
|
||||
|
||||
Toy_runVM(&subVM); //TODO: could use a 'map'-style method by storing the results
|
||||
Toy_runVM(&subVM);
|
||||
|
||||
Toy_resetVM(&subVM, false, true);
|
||||
subVM.scope = NULL; //BUGFIX: need to clear the scope when iterating
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -128,7 +140,7 @@ static void attr_arrayForEach(Toy_VM* vm) {
|
||||
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
|
||||
Toy_pushStack(&subVM.stack, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]));
|
||||
|
||||
fn->native.callback(&subVM); //NOTE: try not to leave anything on the stack afterwards
|
||||
fn->native.callback(&subVM, &fn->native); //NOTE: try not to leave anything on the stack afterwards
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -142,28 +154,30 @@ static void attr_arrayForEach(Toy_VM* vm) {
|
||||
Toy_freeVM(&subVM);
|
||||
}
|
||||
|
||||
static void attr_arraySort(Toy_VM* vm) {
|
||||
static void attr_arraySort(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)vm;
|
||||
(void)self;
|
||||
|
||||
//URGENT: attr_arraySort
|
||||
}
|
||||
|
||||
Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||
if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) {
|
||||
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "pushBack", 8) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "pushBack")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPushBack);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "popBack", 7) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "popBack")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "forEach", 7) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayForEach);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "sort", 4) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "sort")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
@@ -175,7 +189,9 @@ Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_tableInsert(Toy_VM* vm) {
|
||||
static void attr_tableInsert(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
|
||||
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||
Toy_Value value = Toy_popStack(&vm->stack); //NOTE: the args are still backwards, except compound
|
||||
Toy_Value key = Toy_popStack(&vm->stack);
|
||||
@@ -197,7 +213,9 @@ static void attr_tableInsert(Toy_VM* vm) {
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_tableHasKey(Toy_VM* vm) {
|
||||
static void attr_tableHasKey(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
|
||||
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||
Toy_Value key = Toy_popStack(&vm->stack);
|
||||
|
||||
@@ -209,7 +227,9 @@ static void attr_tableHasKey(Toy_VM* vm) {
|
||||
Toy_pushStack(&vm->stack, result);
|
||||
}
|
||||
|
||||
static void attr_tableRemove(Toy_VM* vm) {
|
||||
static void attr_tableRemove(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)self;
|
||||
|
||||
Toy_Value compound = Toy_popStack(&vm->stack);
|
||||
Toy_Value key = Toy_popStack(&vm->stack);
|
||||
|
||||
@@ -218,28 +238,30 @@ static void attr_tableRemove(Toy_VM* vm) {
|
||||
Toy_removeTable(&table, key);
|
||||
}
|
||||
|
||||
static void attr_tableForEach(Toy_VM* vm) {
|
||||
static void attr_tableForEach(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
(void)vm;
|
||||
//URGENT: attr_tableForEach
|
||||
(void)self;
|
||||
|
||||
//URGENT: replace with for-loop
|
||||
}
|
||||
|
||||
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
|
||||
if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "length", 6) == 0) {
|
||||
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "insert", 6) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "insert")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableInsert);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "hasKey", 6) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "hasKey")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableHasKey);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "remove", 6) == 0) {
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "remove")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableRemove);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
else if (strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "forEach", 7) == 0) { //BUG: compare the contents AND length of these strings
|
||||
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableForEach);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
|
||||
+4
-1
@@ -4,6 +4,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//buckets of fun
|
||||
Toy_Bucket* Toy_allocateBucket(unsigned int capacity) {
|
||||
@@ -16,6 +17,8 @@ Toy_Bucket* Toy_allocateBucket(unsigned int capacity) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(bucket, 0, sizeof(Toy_Bucket) + capacity); //zero the memory, to avoid broken header metadata
|
||||
|
||||
//initialize the bucket
|
||||
bucket->next = NULL;
|
||||
bucket->capacity = capacity;
|
||||
@@ -85,7 +88,7 @@ TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle) {
|
||||
gc = false;
|
||||
break;
|
||||
}
|
||||
ptr += (*((int*)(ptr)) ^ 1) + 4; //XOR to remove the 'free' flag from the size
|
||||
ptr += ((*((int*)ptr) | 1) ^ 1) + 4; //OR + XOR to remove the 'free' flag from the size
|
||||
}
|
||||
|
||||
//free this link, if its been entirely released
|
||||
|
||||
@@ -57,3 +57,4 @@ TOY_API void Toy_collectBucketGarbage(Toy_Bucket** bucketHandle);
|
||||
#define TOY_BUCKET_IDEAL (TOY_BUCKET_64KB - sizeof(Toy_Bucket))
|
||||
#endif
|
||||
|
||||
//TODO: check for leaks when freeBucket is called, for debugging
|
||||
+94
-25
@@ -10,7 +10,7 @@
|
||||
#include <string.h>
|
||||
|
||||
//misc. utils
|
||||
static bool checkForChaining(Toy_Ast* ptr) {
|
||||
static bool checkForChainedAssign(Toy_Ast* ptr) {
|
||||
if (ptr == NULL) {
|
||||
return false;
|
||||
}
|
||||
@@ -19,10 +19,16 @@ static bool checkForChaining(Toy_Ast* ptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ptr->type == TOY_AST_UNARY) {
|
||||
if (ptr->unary.flag >= TOY_AST_FLAG_PREFIX_INCREMENT && ptr->unary.flag <= TOY_AST_FLAG_POSTFIX_DECREMENT) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkForChainedInvoke(Toy_Ast* ptr) {
|
||||
if (ptr == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ptr->type == TOY_AST_FN_INVOKE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -220,6 +226,7 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast); //for
|
||||
static void writeBytecodeBody(Toy_Bytecode* mb, Toy_Ast* ast);
|
||||
static unsigned char* collateBytecodeBody(Toy_Bytecode* mb);
|
||||
static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment); //forward declare for chaining of var declarations
|
||||
static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast, bool chainedInvoke);
|
||||
|
||||
static unsigned int writeInstructionValue(Toy_Bytecode** mb, Toy_AstValue ast) {
|
||||
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
||||
@@ -573,6 +580,18 @@ static unsigned int writeInstructionAggregate(Toy_Bytecode** mb, Toy_AstAggregat
|
||||
|
||||
return 1; //leaves only 1 value on the stack
|
||||
}
|
||||
else if (ast.flag == TOY_AST_FLAG_FN_ARGUMENTS) {
|
||||
//BUGFIX: invoking a function as an argument gets messy, so re-count the aggregate elements and discard 'result'
|
||||
int count = ast.left != NULL ? 2 : 1;
|
||||
Toy_Ast* iter = ast.right;
|
||||
|
||||
while (iter != NULL && iter->type == TOY_AST_AGGREGATE) {
|
||||
iter = iter->aggregate.right;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST aggregate flag found\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
@@ -699,6 +718,15 @@ static unsigned int writeInstructionWhileThen(Toy_Bytecode** mb, Toy_AstWhileThe
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionForThen(Toy_Bytecode** mb, Toy_AstForThen ast) {
|
||||
//URGENT: WIP
|
||||
(void)mb;
|
||||
(void)ast;
|
||||
|
||||
(*mb)->panic = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionBreak(Toy_Bytecode** mb, Toy_AstBreak ast) {
|
||||
//unused
|
||||
(void)ast;
|
||||
@@ -748,8 +776,18 @@ static unsigned int writeInstructionContinue(Toy_Bytecode** mb, Toy_AstContinue
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionReturn(Toy_Bytecode** mb, Toy_AstReturn ast) {
|
||||
//the things to return
|
||||
unsigned int retCount = writeBytecodeFromAst(mb, ast.child);
|
||||
unsigned int retCount = 0;
|
||||
|
||||
//what is returned
|
||||
if (checkForChainedAssign(ast.child)) {
|
||||
retCount = writeInstructionAssign(mb, ast.child->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.child)) {
|
||||
retCount = writeInstructionFnInvoke(mb, ast.child->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
retCount = writeBytecodeFromAst(mb, ast.child); //default value
|
||||
}
|
||||
|
||||
//output the print opcode
|
||||
EMIT_BYTE(mb, code,TOY_OPCODE_RETURN);
|
||||
@@ -759,7 +797,7 @@ static unsigned int writeInstructionReturn(Toy_Bytecode** mb, Toy_AstReturn ast)
|
||||
EMIT_BYTE(mb, code,0);
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
return 0;
|
||||
return retCount;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionPrint(Toy_Bytecode** mb, Toy_AstPrint ast) {
|
||||
@@ -779,9 +817,12 @@ static unsigned int writeInstructionPrint(Toy_Bytecode** mb, Toy_AstPrint ast) {
|
||||
|
||||
static unsigned int writeInstructionVarDeclare(Toy_Bytecode** mb, Toy_AstVarDeclare ast) {
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -800,8 +841,6 @@ static unsigned int writeInstructionVarDeclare(Toy_Bytecode** mb, Toy_AstVarDecl
|
||||
static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment) {
|
||||
unsigned int result = 0;
|
||||
|
||||
//BUG: flip the order of target & value, to allow chained assignment AND multiple return values
|
||||
|
||||
//target is a variable name
|
||||
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) {
|
||||
//name string
|
||||
@@ -822,9 +861,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
writeBytecodeFromAst(mb, ast.target->aggregate.right); //key
|
||||
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -847,9 +889,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
//determine RHS, include duplication if needed
|
||||
if (ast.flag == TOY_AST_FLAG_ASSIGN) {
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -866,9 +911,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -885,9 +933,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -904,9 +955,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -923,9 +977,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -942,9 +999,12 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
//if we're dealing with chained assignments, hijack the next assignment with 'chainedAssignment' set to true
|
||||
if (checkForChaining(ast.expr)) {
|
||||
if (checkForChainedAssign(ast.expr)) {
|
||||
result += writeInstructionAssign(mb, ast.expr->varAssign, true);
|
||||
}
|
||||
else if (checkForChainedInvoke(ast.expr)) {
|
||||
result += writeInstructionFnInvoke(mb, ast.expr->fnInvoke, true);
|
||||
}
|
||||
else {
|
||||
result += writeBytecodeFromAst(mb, ast.expr); //default value
|
||||
}
|
||||
@@ -960,7 +1020,7 @@ static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign a
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return result + (chainedAssignment ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionAccess(Toy_Bytecode** mb, Toy_AstVarAccess ast) {
|
||||
@@ -1054,7 +1114,7 @@ static unsigned int writeInstructionFnDeclare(Toy_Bytecode** mb, Toy_AstFnDeclar
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast) {
|
||||
static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast, bool chainedInvoke) {
|
||||
unsigned int argCount = writeBytecodeFromAst(mb, ast.args);
|
||||
|
||||
if (argCount > 255) {
|
||||
@@ -1075,9 +1135,9 @@ static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke
|
||||
EMIT_BYTE(mb, code, TOY_OPCODE_INVOKE);
|
||||
EMIT_BYTE(mb, code, TOY_VALUE_FUNCTION);
|
||||
EMIT_BYTE(mb, code, (unsigned char)argCount);
|
||||
EMIT_BYTE(mb, code, 0); //IDK how many returns
|
||||
EMIT_BYTE(mb, code, 0); //BUG: IDK how many returns
|
||||
|
||||
return 0;
|
||||
return chainedInvoke ? 1 : 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionAttribute(Toy_Bytecode** mb, Toy_AstAttribute ast) {
|
||||
@@ -1193,6 +1253,10 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
|
||||
result += writeInstructionWhileThen(mb, ast->whileThen);
|
||||
break;
|
||||
|
||||
case TOY_AST_FOR_THEN:
|
||||
result += writeInstructionForThen(mb, ast->forThen);
|
||||
break;
|
||||
|
||||
case TOY_AST_BREAK:
|
||||
result += writeInstructionBreak(mb, ast->breakPoint);
|
||||
break;
|
||||
@@ -1226,13 +1290,19 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
|
||||
break;
|
||||
|
||||
case TOY_AST_FN_INVOKE:
|
||||
result += writeInstructionFnInvoke(mb, ast->fnInvoke);
|
||||
result += writeInstructionFnInvoke(mb, ast->fnInvoke, false);
|
||||
break;
|
||||
|
||||
case TOY_AST_ATTRIBUTE:
|
||||
result += writeInstructionAttribute(mb, ast->attribute);
|
||||
break;
|
||||
|
||||
case TOY_AST_ITERABLE:
|
||||
//the 'in' keyword is only valid within a for-loop's condition
|
||||
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: the 'in' keyword is only valid within a for-loop's condition\n" TOY_CC_RESET);
|
||||
(*mb)->panic = true;
|
||||
break;
|
||||
|
||||
case TOY_AST_STACK_POP:
|
||||
result += writeInstructionStackPop(mb, ast->stackPop);
|
||||
break;
|
||||
@@ -1248,8 +1318,7 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
|
||||
break;
|
||||
|
||||
case TOY_AST_END:
|
||||
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Unknown 'end'\n" TOY_CC_RESET);
|
||||
(*mb)->panic = true;
|
||||
//NO-OP
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+18
-9
@@ -16,6 +16,8 @@ Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nati
|
||||
|
||||
fn->type = TOY_FUNCTION_NATIVE;
|
||||
fn->native.callback = callback;
|
||||
fn->native.meta1 = 0; //BUGFIX: Workaround for native functions lacking access to a closure-like scope
|
||||
fn->native.meta2 = 0;
|
||||
|
||||
return fn;
|
||||
}
|
||||
@@ -23,15 +25,22 @@ Toy_Function* Toy_createFunctionFromCallback(Toy_Bucket** bucketHandle, Toy_nati
|
||||
Toy_Function* Toy_copyFunction(Toy_Bucket** bucketHandle, Toy_Function* original) {
|
||||
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
|
||||
|
||||
if (original->type == TOY_FUNCTION_CUSTOM) {
|
||||
fn->type = original->type;
|
||||
fn->bytecode.code = original->bytecode.code;
|
||||
fn->bytecode.parentScope = original->bytecode.parentScope;
|
||||
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
|
||||
}
|
||||
else if (fn->type == TOY_FUNCTION_NATIVE) {
|
||||
fn->type = original->type;
|
||||
fn->native.callback = original->native.callback;
|
||||
switch(original->type) {
|
||||
case TOY_FUNCTION_CUSTOM: {
|
||||
fn->type = original->type;
|
||||
fn->bytecode.code = original->bytecode.code;
|
||||
fn->bytecode.parentScope = original->bytecode.parentScope;
|
||||
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOY_FUNCTION_NATIVE: {
|
||||
fn->type = original->type;
|
||||
fn->native.callback = original->native.callback;
|
||||
fn->native.meta1 = original->native.meta1;
|
||||
fn->native.meta2 = original->native.meta2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return fn;
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
|
||||
//forward declare
|
||||
struct Toy_VM;
|
||||
typedef void (*Toy_nativeCallback)(struct Toy_VM*);
|
||||
struct Toy_FunctionNative;
|
||||
|
||||
typedef void (*Toy_nativeCallback)(struct Toy_VM*, struct Toy_FunctionNative* self);
|
||||
|
||||
typedef enum Toy_FunctionType {
|
||||
TOY_FUNCTION_CUSTOM,
|
||||
@@ -23,6 +25,8 @@ typedef struct Toy_FunctionBytecode {
|
||||
typedef struct Toy_FunctionNative {
|
||||
Toy_FunctionType type;
|
||||
Toy_nativeCallback callback;
|
||||
int meta1;
|
||||
int meta2;
|
||||
} Toy_FunctionNative;
|
||||
|
||||
typedef union Toy_Function_t {
|
||||
|
||||
@@ -12,6 +12,7 @@ typedef enum Toy_OpcodeType {
|
||||
TOY_OPCODE_ACCESS,
|
||||
TOY_OPCODE_INVOKE, //for calling functions
|
||||
TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds
|
||||
TOY_OPCODE_ITERABLE, //for operating on all members of a compound
|
||||
TOY_OPCODE_DUPLICATE, //duplicate the top of the stack
|
||||
TOY_OPCODE_ELIMINATE, //remove the top of the stack
|
||||
|
||||
|
||||
+48
-9
@@ -122,6 +122,7 @@ static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
|
||||
static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||
static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||
static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||
static Toy_AstFlag iterable(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||
|
||||
//precedence definitions
|
||||
static ParsingTuple parsingRulesetTable[] = {
|
||||
@@ -156,7 +157,7 @@ static ParsingTuple parsingRulesetTable[] = {
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_FUNCTION,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IF,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IMPORT,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IN,
|
||||
{PREC_CALL,NULL,iterable},// TOY_TOKEN_KEYWORD_IN,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_OF,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PASS,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PRINT,
|
||||
@@ -724,7 +725,7 @@ static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
|
||||
parsePrecedence(bucketHandle, parser, &ast, PREC_GROUP);
|
||||
|
||||
//add to the args aggregate (is added backwards, because weird)
|
||||
Toy_private_emitAstAggregate(bucketHandle, &args, TOY_AST_FLAG_COLLECTION, ast);
|
||||
Toy_private_emitAstAggregate(bucketHandle, &args, TOY_AST_FLAG_FN_ARGUMENTS, ast);
|
||||
}
|
||||
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' at the end of argument list");
|
||||
@@ -760,6 +761,19 @@ static Toy_AstFlag attribute(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
|
||||
}
|
||||
}
|
||||
|
||||
static Toy_AstFlag iterable(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
//infix must advance
|
||||
advance(parser);
|
||||
|
||||
Toy_Ast* expr = NULL;
|
||||
parsePrecedence(bucketHandle, parser, &expr, PREC_CALL);
|
||||
Toy_private_emitAstIterable(bucketHandle, rootHandle, expr);
|
||||
|
||||
//TODO: check for var declare without assignment
|
||||
|
||||
return TOY_AST_FLAG_NONE;
|
||||
}
|
||||
|
||||
//grammar rules
|
||||
static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule) {
|
||||
//'step over' the token to parse
|
||||
@@ -865,7 +879,7 @@ static void makeIfThenElseStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, To
|
||||
makeExpr(bucketHandle, parser, &condBranch);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'if' condition");
|
||||
|
||||
// { thenBranch }
|
||||
//{ thenBranch }
|
||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||
|
||||
//else { elseBranch }
|
||||
@@ -885,12 +899,29 @@ static void makeWhileStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
|
||||
makeExpr(bucketHandle, parser, &condBranch);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'while' condition");
|
||||
|
||||
// { thenBranch }
|
||||
//{ thenBranch }
|
||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||
|
||||
Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch);
|
||||
}
|
||||
|
||||
static void makeForStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
Toy_Ast* condBranch = NULL;
|
||||
Toy_Ast* thenBranch = NULL;
|
||||
|
||||
//for (condBranch)
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_LEFT, "Expected '(' after 'for' keyword");
|
||||
makeExpr(bucketHandle, parser, &condBranch);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'for' condition");
|
||||
|
||||
//TODO: check for an iterable node
|
||||
|
||||
//{ thenBranch }
|
||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||
|
||||
Toy_private_emitAstForThen(bucketHandle, rootHandle, condBranch, thenBranch);
|
||||
}
|
||||
|
||||
static void makeBreakStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
Toy_private_emitAstBreak(bucketHandle, rootHandle);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of break statement");
|
||||
@@ -902,10 +933,14 @@ static void makeContinueStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
|
||||
}
|
||||
|
||||
static void makeReturnStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //expect an aggregate
|
||||
Toy_private_emitAstReturn(bucketHandle, rootHandle);
|
||||
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of return statement");
|
||||
if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
||||
Toy_private_emitAstReturn(bucketHandle, rootHandle);
|
||||
}
|
||||
else {
|
||||
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //expect an aggregate
|
||||
Toy_private_emitAstReturn(bucketHandle, rootHandle);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of return statement");
|
||||
}
|
||||
}
|
||||
|
||||
static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
@@ -1066,7 +1101,11 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: for-pre-clause-post-then
|
||||
//for
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_FOR)) {
|
||||
makeForStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//break
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_BREAK)) {
|
||||
|
||||
+19
-22
@@ -19,12 +19,12 @@ static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, un
|
||||
|
||||
while (true) {
|
||||
//found the entry
|
||||
if (Toy_compareStrings(&(scope->data[probe].key), key) == 0) {
|
||||
if (scope->data[probe].key != NULL && Toy_compareStrings(scope->data[probe].key, key) == 0) {
|
||||
return &(scope->data[probe]);
|
||||
}
|
||||
|
||||
//if its an empty slot (didn't find it here)
|
||||
if (scope->data[probe].key.info.length == 0) {
|
||||
if (scope->data[probe].key == NULL) {
|
||||
return recursive ? lookupScopeEntryPtr(scope->next, key, hash, recursive) : NULL;
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, un
|
||||
static void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_ValueType type, bool constant) {
|
||||
//make the entry
|
||||
unsigned int probe = Toy_hashString(key) % scope->capacity;
|
||||
Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = *key, .value = value, .type = type, .constant = constant, .psl = 1 };
|
||||
Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = key, .value = value, .type = type, .constant = constant, .psl = 1 };
|
||||
|
||||
//probe
|
||||
while (true) {
|
||||
//if we're overriding an existing value
|
||||
if (Toy_compareStrings(&(scope->data[probe].key), &(entry.key)) == 0) {
|
||||
if (scope->data[probe].key != NULL && Toy_compareStrings(scope->data[probe].key, entry.key) == 0) {
|
||||
scope->data[probe] = entry;
|
||||
scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl;
|
||||
return;
|
||||
@@ -71,7 +71,7 @@ static void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, T
|
||||
|
||||
static Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapacity) {
|
||||
//allocate and zero a new Toy_ScopeEntry array in memory
|
||||
Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry)); //URGENT: Swap to a bucket
|
||||
Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry));
|
||||
|
||||
if (newEntries == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space for 'Toy_Scope' entries\n" TOY_CC_RESET);
|
||||
@@ -94,8 +94,8 @@ static Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapa
|
||||
|
||||
//for each existing entry in the old array, copy it into the new array
|
||||
for (unsigned int i = 0; i < oldCapacity; i++) {
|
||||
if (oldEntries[i].key.info.length > 0) {
|
||||
probeAndInsert(scope, &(oldEntries[i].key), oldEntries[i].value, oldEntries[i].type, oldEntries[i].constant);
|
||||
if (oldEntries[i].key != NULL && oldEntries[i].key->info.length > 0) {
|
||||
probeAndInsert(scope, oldEntries[i].key, oldEntries[i].value, oldEntries[i].type, oldEntries[i].constant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,10 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
||||
}
|
||||
|
||||
Toy_private_decrementScopeRefCount(scope);
|
||||
return scope->next;
|
||||
|
||||
Toy_Scope* next = scope->next;
|
||||
Toy_releaseBucketPartition((void*)scope);
|
||||
return next;
|
||||
}
|
||||
|
||||
void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant) {
|
||||
@@ -193,6 +196,8 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_freeValue(entryPtr->value);
|
||||
|
||||
entryPtr->value = value;
|
||||
}
|
||||
|
||||
@@ -227,29 +232,21 @@ void Toy_private_incrementScopeRefCount(Toy_Scope* scope) {
|
||||
}
|
||||
|
||||
void Toy_private_decrementScopeRefCount(Toy_Scope* scope) {
|
||||
Toy_Scope* iter = scope;
|
||||
|
||||
while (iter) {
|
||||
for (Toy_Scope* iter = scope; iter != NULL; iter = iter->next) {
|
||||
iter->refCount--;
|
||||
|
||||
//clean up our insides if needed
|
||||
if (iter->refCount == 0) {
|
||||
//free the scope entries when this scope is no longer needed
|
||||
//free the data
|
||||
if (iter->data != NULL) {
|
||||
for (unsigned int i = 0; i < iter->capacity; i++) {
|
||||
if (iter->data[i].psl > 0) {
|
||||
Toy_freeString(&(iter->data[i].key));
|
||||
if (iter->data[i].key != NULL) {
|
||||
Toy_freeString(iter->data[i].key);
|
||||
Toy_freeValue(iter->data[i].value);
|
||||
}
|
||||
}
|
||||
free(iter->data);
|
||||
}
|
||||
|
||||
//free the scope itself, fixing the iterator for the next loop
|
||||
Toy_Scope* empty = iter;
|
||||
iter = iter->next;
|
||||
Toy_releaseBucketPartition((void*)empty);
|
||||
}
|
||||
else {
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
//keys are leaf-only strings
|
||||
typedef struct Toy_ScopeEntry {
|
||||
Toy_String key;
|
||||
Toy_String* key;
|
||||
Toy_Value value;
|
||||
Toy_ValueType type;
|
||||
unsigned int psl; //psl '0' means empty
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Toy_Stack* Toy_allocateStack(void) { //TODO: add initial size as parameter
|
||||
Toy_Stack* stack = malloc(TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack)); //URGENT: Swap to a bucket (4 instances)
|
||||
Toy_Stack* Toy_allocateStack(void) {
|
||||
Toy_Stack* stack = malloc(TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack));
|
||||
|
||||
if (stack == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, TOY_STACK_INITIAL_CAPACITY, (int)(TOY_STACK_INITIAL_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack)));
|
||||
|
||||
@@ -20,10 +20,18 @@ static void incrementRefCount(Toy_String* str) {
|
||||
static void decrementRefCount(Toy_String* str) {
|
||||
str->info.refCount--;
|
||||
if (str->info.type == TOY_STRING_NODE) {
|
||||
//the parent of this node triggered a decrement across the whole tree
|
||||
decrementRefCount(str->node.left);
|
||||
decrementRefCount(str->node.right);
|
||||
}
|
||||
if (str->info.refCount == 0) {
|
||||
if (str->info.type == TOY_STRING_NODE) {
|
||||
//THIS node has triggered the decrement, so run this again
|
||||
decrementRefCount(str->node.left);
|
||||
decrementRefCount(str->node.right);
|
||||
}
|
||||
|
||||
//mark this memory as unused
|
||||
Toy_releaseBucketPartition((void*)str);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ static void probeAndInsert(Toy_Table** tableHandle, Toy_Value key, Toy_Value val
|
||||
//exposed functions
|
||||
Toy_Table* Toy_private_adjustTableCapacity(Toy_Table* oldTable, unsigned int newCapacity) {
|
||||
//allocate and zero a new table in memory
|
||||
Toy_Table* newTable = malloc(newCapacity * sizeof(Toy_TableEntry) + sizeof(Toy_Table)); //URGENT: Swap to a bucket
|
||||
Toy_Table* newTable = malloc(newCapacity * sizeof(Toy_TableEntry) + sizeof(Toy_Table));
|
||||
|
||||
if (newTable == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Table'\n" TOY_CC_RESET);
|
||||
|
||||
+31
-31
@@ -20,28 +20,28 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_TYPE_ANY,
|
||||
|
||||
//keywords and reserved words
|
||||
TOY_TOKEN_KEYWORD_AS,
|
||||
TOY_TOKEN_KEYWORD_AS, //unused
|
||||
TOY_TOKEN_KEYWORD_ASSERT,
|
||||
TOY_TOKEN_KEYWORD_BREAK,
|
||||
TOY_TOKEN_KEYWORD_CLASS,
|
||||
TOY_TOKEN_KEYWORD_CLASS, //unused
|
||||
TOY_TOKEN_KEYWORD_CONST,
|
||||
TOY_TOKEN_KEYWORD_CONTINUE,
|
||||
TOY_TOKEN_KEYWORD_DO,
|
||||
TOY_TOKEN_KEYWORD_DO, //unused
|
||||
TOY_TOKEN_KEYWORD_ELSE,
|
||||
TOY_TOKEN_KEYWORD_EXPORT,
|
||||
TOY_TOKEN_KEYWORD_EXPORT, //unused
|
||||
TOY_TOKEN_KEYWORD_FOR,
|
||||
TOY_TOKEN_KEYWORD_FOREACH,
|
||||
TOY_TOKEN_KEYWORD_FUNCTION,
|
||||
TOY_TOKEN_KEYWORD_FOREACH, //unused
|
||||
TOY_TOKEN_KEYWORD_FUNCTION, //remapped 'fn'
|
||||
TOY_TOKEN_KEYWORD_IF,
|
||||
TOY_TOKEN_KEYWORD_IMPORT,
|
||||
TOY_TOKEN_KEYWORD_IN,
|
||||
TOY_TOKEN_KEYWORD_OF,
|
||||
TOY_TOKEN_KEYWORD_IMPORT, //unused
|
||||
TOY_TOKEN_KEYWORD_IN, //unused
|
||||
TOY_TOKEN_KEYWORD_OF, //unused
|
||||
TOY_TOKEN_KEYWORD_PASS,
|
||||
TOY_TOKEN_KEYWORD_PRINT,
|
||||
TOY_TOKEN_KEYWORD_RETURN,
|
||||
TOY_TOKEN_KEYWORD_VAR,
|
||||
TOY_TOKEN_KEYWORD_WHILE,
|
||||
TOY_TOKEN_KEYWORD_YIELD,
|
||||
TOY_TOKEN_KEYWORD_YIELD, //unused
|
||||
|
||||
//literal values
|
||||
TOY_TOKEN_LITERAL_TRUE,
|
||||
@@ -51,19 +51,19 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
TOY_TOKEN_OPERATOR_ADD,
|
||||
TOY_TOKEN_OPERATOR_SUBTRACT,
|
||||
TOY_TOKEN_OPERATOR_MULTIPLY,
|
||||
TOY_TOKEN_OPERATOR_DIVIDE,
|
||||
TOY_TOKEN_OPERATOR_MODULO,
|
||||
TOY_TOKEN_OPERATOR_ADD_ASSIGN,
|
||||
TOY_TOKEN_OPERATOR_SUBTRACT_ASSIGN,
|
||||
TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN,
|
||||
TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN,
|
||||
TOY_TOKEN_OPERATOR_MODULO_ASSIGN,
|
||||
TOY_TOKEN_OPERATOR_INCREMENT,
|
||||
TOY_TOKEN_OPERATOR_DECREMENT,
|
||||
TOY_TOKEN_OPERATOR_ASSIGN,
|
||||
TOY_TOKEN_OPERATOR_ADD, // +
|
||||
TOY_TOKEN_OPERATOR_SUBTRACT, // -
|
||||
TOY_TOKEN_OPERATOR_MULTIPLY, // *
|
||||
TOY_TOKEN_OPERATOR_DIVIDE, // /
|
||||
TOY_TOKEN_OPERATOR_MODULO, // %
|
||||
TOY_TOKEN_OPERATOR_ADD_ASSIGN, // +=
|
||||
TOY_TOKEN_OPERATOR_SUBTRACT_ASSIGN, // -=
|
||||
TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN, // *=
|
||||
TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN, // /=
|
||||
TOY_TOKEN_OPERATOR_MODULO_ASSIGN, // %=
|
||||
TOY_TOKEN_OPERATOR_INCREMENT, // ++
|
||||
TOY_TOKEN_OPERATOR_DECREMENT, // --
|
||||
TOY_TOKEN_OPERATOR_ASSIGN, // =
|
||||
|
||||
//comparator operators
|
||||
TOY_TOKEN_OPERATOR_COMPARE_EQUAL, // ==
|
||||
@@ -74,12 +74,12 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_OPERATOR_COMPARE_GREATER_EQUAL, // >=
|
||||
|
||||
//structural operators
|
||||
TOY_TOKEN_OPERATOR_PAREN_LEFT,
|
||||
TOY_TOKEN_OPERATOR_PAREN_RIGHT,
|
||||
TOY_TOKEN_OPERATOR_BRACKET_LEFT,
|
||||
TOY_TOKEN_OPERATOR_BRACKET_RIGHT,
|
||||
TOY_TOKEN_OPERATOR_BRACE_LEFT,
|
||||
TOY_TOKEN_OPERATOR_BRACE_RIGHT,
|
||||
TOY_TOKEN_OPERATOR_PAREN_LEFT, // (
|
||||
TOY_TOKEN_OPERATOR_PAREN_RIGHT, // )
|
||||
TOY_TOKEN_OPERATOR_BRACKET_LEFT, // [
|
||||
TOY_TOKEN_OPERATOR_BRACKET_RIGHT, // ]
|
||||
TOY_TOKEN_OPERATOR_BRACE_LEFT, // {
|
||||
TOY_TOKEN_OPERATOR_BRACE_RIGHT, // }
|
||||
|
||||
//other operators
|
||||
TOY_TOKEN_OPERATOR_AND, // &&
|
||||
@@ -100,7 +100,7 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_OPERATOR_PIPE, // |
|
||||
|
||||
//meta tokens
|
||||
TOY_TOKEN_ERROR,
|
||||
TOY_TOKEN_EOF,
|
||||
TOY_TOKEN_ERROR, //meta
|
||||
TOY_TOKEN_EOF, //meta
|
||||
} Toy_TokenType;
|
||||
|
||||
|
||||
+3
-6
@@ -104,7 +104,7 @@ Toy_Value Toy_copyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
return TOY_VALUE_FROM_STRING(Toy_copyString(value.as.string));
|
||||
}
|
||||
|
||||
case TOY_VALUE_ARRAY: { //TODO: switch to buckets
|
||||
case TOY_VALUE_ARRAY: {
|
||||
//arrays probably won't get copied much
|
||||
Toy_Array* ptr = value.as.array;
|
||||
Toy_Array* result = Toy_resizeArray(NULL, ptr->capacity);
|
||||
@@ -119,7 +119,7 @@ Toy_Value Toy_copyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
return TOY_VALUE_FROM_ARRAY(result);
|
||||
}
|
||||
|
||||
case TOY_VALUE_TABLE: { //TODO: switch to buckets
|
||||
case TOY_VALUE_TABLE: {
|
||||
//tables probably won't get copied much
|
||||
Toy_Table* ptr = value.as.table;
|
||||
Toy_Table* result = Toy_allocateTable(ptr->capacity);
|
||||
@@ -494,7 +494,6 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
return Toy_copyString(value.as.string);
|
||||
|
||||
case TOY_VALUE_ARRAY: {
|
||||
//TODO: concat + free is definitely a performance nightmare, could make an append function?
|
||||
Toy_Array* ptr = value.as.array;
|
||||
|
||||
//if array is empty, skip below
|
||||
@@ -550,7 +549,7 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
//clean up
|
||||
Toy_freeString(open);
|
||||
Toy_freeString(close);
|
||||
Toy_freeString(comma); //TODO: reusable global, or string type "permanent", needs benchmarking
|
||||
Toy_freeString(comma);
|
||||
Toy_freeString(quote);
|
||||
|
||||
return string;
|
||||
@@ -626,8 +625,6 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
//finally
|
||||
string = final;
|
||||
|
||||
//TODO: would a simple buffer be faster here?
|
||||
|
||||
//if there's more elements
|
||||
needsComma = true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS
|
||||
#define TOY_VALUE_IS_ARRAY(value) ((value).type == TOY_VALUE_ARRAY || (TOY_VALUE_IS_REFERENCE(value) && Toy_unwrapValue(value).type == TOY_VALUE_ARRAY))
|
||||
#define TOY_VALUE_IS_TABLE(value) ((value).type == TOY_VALUE_TABLE || (TOY_VALUE_IS_REFERENCE(value) && Toy_unwrapValue(value).type == TOY_VALUE_TABLE))
|
||||
#define TOY_VALUE_IS_FUNCTION(value) ((value).type == TOY_VALUE_FUNCTION || (TOY_VALUE_IS_REFERENCE(value) && Toy_unwrapValue(value).type == TOY_VALUE_FUNCTION))
|
||||
#define TOY_VALUE_IS_OPAQUE(value) ((value).type == TOY_VALUE_OPAQUE)
|
||||
#define TOY_VALUE_IS_OPAQUE(value) ((value).type == TOY_VALUE_OPAQUE || (TOY_VALUE_IS_REFERENCE(value) && Toy_unwrapValue(value).type == TOY_VALUE_OPAQUE))
|
||||
#define TOY_VALUE_IS_REFERENCE(value) ((value).type == TOY_VALUE_REFERENCE)
|
||||
|
||||
#define TOY_VALUE_AS_BOOLEAN(value) ((value).as.boolean)
|
||||
|
||||
+14
-27
@@ -210,7 +210,7 @@ static void processAssign(Toy_VM* vm) {
|
||||
Toy_Value name = Toy_popStack(&vm->stack);
|
||||
|
||||
//assign it
|
||||
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), Toy_copyValue(&vm->memoryBucket, value)); //scope now owns the value, doesn't need to be freed
|
||||
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), Toy_copyValue(&vm->memoryBucket, value));
|
||||
|
||||
//in case of chaining, leave a copy on the stack
|
||||
bool chainedAssignment = READ_BYTE(vm);
|
||||
@@ -220,6 +220,7 @@ static void processAssign(Toy_VM* vm) {
|
||||
|
||||
//cleanup
|
||||
Toy_freeValue(name);
|
||||
Toy_freeValue(value);
|
||||
}
|
||||
|
||||
static void processAssignCompound(Toy_VM* vm) {
|
||||
@@ -393,15 +394,8 @@ static void processInvoke(Toy_VM* vm) {
|
||||
|
||||
//extract and store any results
|
||||
if (resultCount > 0) {
|
||||
Toy_Array* results = Toy_extractResultsFromVM(vm, &subVM, resultCount);
|
||||
|
||||
for (unsigned int i = 0; i < results->count; i++) {
|
||||
//NOTE: since the results array is being immediately freed, just push each element without a call to copy
|
||||
Toy_pushStack(&vm->stack, results->data[i]);
|
||||
}
|
||||
|
||||
//a bit naughty
|
||||
free(results);
|
||||
Toy_Value result = Toy_getReturnValueFromVM(vm, &subVM);
|
||||
Toy_pushStack(&vm->stack, result);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
@@ -411,7 +405,7 @@ static void processInvoke(Toy_VM* vm) {
|
||||
|
||||
case TOY_FUNCTION_NATIVE: {
|
||||
//NOTE: arguments are on the stack, leave results on the stack
|
||||
fn->native.callback(vm);
|
||||
fn->native.callback(vm, &fn->native);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -724,7 +718,7 @@ static void processAssert(Toy_VM* vm) {
|
||||
|
||||
//determine the args
|
||||
if (count == 1) {
|
||||
message = TOY_VALUE_FROM_STRING(Toy_toString(&vm->memoryBucket, "assertion failed")); //TODO: needs a better default message
|
||||
message = TOY_VALUE_FROM_STRING(Toy_toString(&vm->memoryBucket, "assertion failed")); //TODO: better default error message
|
||||
value = Toy_popStack(&vm->stack);
|
||||
}
|
||||
else if (count == 2) {
|
||||
@@ -1066,6 +1060,7 @@ static unsigned int process(Toy_VM* vm) {
|
||||
processIndex(vm);
|
||||
break;
|
||||
|
||||
case TOY_OPCODE_ITERABLE: //tmp
|
||||
case TOY_OPCODE_UNUSED:
|
||||
case TOY_OPCODE_PASS:
|
||||
case TOY_OPCODE_ERROR:
|
||||
@@ -1098,12 +1093,12 @@ void Toy_resetVM(Toy_VM* vm, bool preserveScope, bool preserveStack) {
|
||||
}
|
||||
|
||||
if (!preserveStack) {
|
||||
Toy_resetStack(&vm->stack); //NOTE: has a realloc()
|
||||
Toy_resetStack(&vm->stack); //WARN: has a realloc()
|
||||
}
|
||||
|
||||
//not sure how often to call teh GC
|
||||
if (vm->memoryBucket) {
|
||||
Toy_collectBucketGarbage(&vm->memoryBucket);
|
||||
Toy_collectBucketGarbage(&vm->memoryBucket); //WONTFIX: call GC after a certain number of bucket links allocated
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1188,19 +1183,11 @@ void Toy_freeVM(Toy_VM* vm) {
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Array* Toy_extractResultsFromVM(Toy_VM* parentVM, Toy_VM* subVM, unsigned int resultCount) {
|
||||
if (subVM->stack->count < resultCount) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Too many results requested from VM, exiting\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
Toy_Value Toy_getReturnValueFromVM(Toy_VM* parentVM, Toy_VM* subVM) {
|
||||
if (subVM->stack->count > 0) {
|
||||
return Toy_copyValue(&parentVM->memoryBucket, subVM->stack->data[subVM->stack->count-1]);
|
||||
}
|
||||
|
||||
Toy_Array* results = Toy_resizeArray(NULL, resultCount);
|
||||
|
||||
const unsigned int offset = subVM->stack->count - resultCount; //first element to extract
|
||||
|
||||
for (/* EMPTY */; results->count < resultCount; results->count++) { //TODO: make sure the parent bucket adopts the child bucket's responsibilities
|
||||
results->data[results->count] = Toy_copyValue(&parentVM->memoryBucket, subVM->stack->data[offset + results->count]);
|
||||
else {
|
||||
return TOY_VALUE_FROM_NULL();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,5 +51,5 @@ void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, Toy_Scope* parentScope);
|
||||
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
|
||||
TOY_API void Toy_freeVM(Toy_VM* vm);
|
||||
|
||||
TOY_API Toy_Array* Toy_extractResultsFromVM(Toy_VM* parentVM, Toy_VM* subVM, unsigned int resultCount);
|
||||
TOY_API Toy_Value Toy_getReturnValueFromVM(Toy_VM* parentVM, Toy_VM* subVM);
|
||||
|
||||
|
||||
@@ -24,5 +24,3 @@ print a;
|
||||
var b = [];
|
||||
|
||||
print b;
|
||||
|
||||
//TODO: utility functions are needed, see toy_array.h
|
||||
@@ -0,0 +1 @@
|
||||
//TODO: empty test script
|
||||
@@ -1,9 +0,0 @@
|
||||
//these are allowed
|
||||
/* EMPTY */;
|
||||
if (true) { }
|
||||
if (true) pass;
|
||||
|
||||
|
||||
//these are not allowed
|
||||
// if (true) /* EMPTY */;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
fn a(x) {
|
||||
assert x == 42;
|
||||
}
|
||||
|
||||
fn b() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
a(b());
|
||||
@@ -0,0 +1 @@
|
||||
//TODO: empty test script
|
||||
@@ -11,3 +11,5 @@ assert 1 < 2;
|
||||
|
||||
//assert with an optional message
|
||||
assert true, "Assertion message";
|
||||
|
||||
//TODO: add mustfails
|
||||
@@ -1,65 +1,62 @@
|
||||
//literals
|
||||
if (true) {
|
||||
print "Success 1";
|
||||
assert true, "if-then-else 1";
|
||||
}
|
||||
else {
|
||||
print "Failure 1";
|
||||
assert false, "if-then-else 1";
|
||||
}
|
||||
|
||||
//false literals
|
||||
if (false) {
|
||||
print "Failure 2";
|
||||
assert false, "if-then-else 2";
|
||||
}
|
||||
else {
|
||||
print "Success 2";
|
||||
assert true, "if-then-else 2";
|
||||
}
|
||||
|
||||
//conditionals
|
||||
if (1 < 2) {
|
||||
print "Success 3";
|
||||
assert true, "if-then-else 3";
|
||||
}
|
||||
if (1 > 2) {
|
||||
print "Failure 3";
|
||||
assert false, "if-then-else 3";
|
||||
}
|
||||
|
||||
|
||||
//variables
|
||||
var a = 42;
|
||||
|
||||
if (a) {
|
||||
print "Success 4";
|
||||
assert true, "if-then-else 4";
|
||||
}
|
||||
else {
|
||||
print "Failure 4";
|
||||
assert false, "if-then-else 4";
|
||||
}
|
||||
|
||||
|
||||
if (a == 42) {
|
||||
print "Success 5";
|
||||
assert true, "if-then-else 5";
|
||||
}
|
||||
else {
|
||||
print "Failure 5";
|
||||
assert false, "if-then-else 5";
|
||||
}
|
||||
|
||||
//concatenated strings
|
||||
if ("foo" .. "bar" == "foobar") {
|
||||
print "Success 6";
|
||||
assert true, "if-then-else 6";
|
||||
}
|
||||
else {
|
||||
print "Failure 6";
|
||||
assert false, "if-then-else 6";
|
||||
}
|
||||
|
||||
|
||||
if ("foobar" == "foo" .. "bar") {
|
||||
print "Success 7";
|
||||
assert true, "if-then-else 7";
|
||||
}
|
||||
else {
|
||||
print "Failure 7";
|
||||
assert false, "if-then-else 7";
|
||||
}
|
||||
|
||||
if ("fizz" .. "le" == "fi" .. "zzle") {
|
||||
print "Success 8";
|
||||
assert true, "if-then-else 8";
|
||||
}
|
||||
else {
|
||||
print "Failure 8";
|
||||
assert false, "if-then-else 8";
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
//literals
|
||||
if (true)
|
||||
print "Success 1";
|
||||
assert true, "if-then-else 1";
|
||||
else
|
||||
print "Failure 1";
|
||||
assert false, "if-then-else 1";
|
||||
|
||||
//false literals
|
||||
if (false)
|
||||
print "Failure 2";
|
||||
assert false, "if-then-else 2";
|
||||
else
|
||||
print "Success 2";
|
||||
assert true, "if-then-else 2";
|
||||
|
||||
//conditionals
|
||||
if (1 < 2)
|
||||
print "Success 3";
|
||||
assert true, "if-then-else 3";
|
||||
if (1 > 2)
|
||||
print "Failure 3";
|
||||
assert false, "if-then-else 3";
|
||||
|
||||
//variables
|
||||
var a = 42;
|
||||
|
||||
if (a)
|
||||
print "Success 4";
|
||||
assert true, "if-then-else 4";
|
||||
else
|
||||
print "Failure 4";
|
||||
assert false, "if-then-else 4";
|
||||
|
||||
if (a == 42)
|
||||
print "Success 5";
|
||||
assert true, "if-then-else 5";
|
||||
else
|
||||
print "Failure 5";
|
||||
assert false, "if-then-else 5";
|
||||
|
||||
//concatenated strings
|
||||
if ("foo" .. "bar" == "foobar")
|
||||
print "Success 6";
|
||||
assert true, "if-then-else 6";
|
||||
else
|
||||
print "Failure 6";
|
||||
assert false, "if-then-else 6";
|
||||
|
||||
if ("foobar" == "foo" .. "bar")
|
||||
print "Success 7";
|
||||
assert true, "if-then-else 7";
|
||||
else
|
||||
print "Failure 7";
|
||||
assert false, "if-then-else 7";
|
||||
|
||||
if ("fizz" .. "le" == "fi" .. "zzle")
|
||||
print "Success 8";
|
||||
assert true, "if-then-else 8";
|
||||
else
|
||||
print "Failure 8";
|
||||
assert false, "if-then-else 8";
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pass;
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
fn empty() { //BUG: there's an extra return in the bytecode
|
||||
return;
|
||||
}
|
||||
|
||||
fn full() {
|
||||
return 42;
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
//shadowing
|
||||
var answer = 42;
|
||||
print answer; //42
|
||||
|
||||
assert answer == 42;
|
||||
{
|
||||
var answer = 7;
|
||||
print answer; //7
|
||||
assert answer == 7;
|
||||
}
|
||||
print answer; //42
|
||||
assert answer == 42;
|
||||
|
||||
//rebinding
|
||||
var question = 42;
|
||||
print question; //42
|
||||
assert question == 42;
|
||||
{
|
||||
var question = question;
|
||||
print question; //42
|
||||
assert question == 42;
|
||||
}
|
||||
print question; //42
|
||||
assert question == 42;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
//This file left intentionally blank
|
||||
@@ -5,8 +5,6 @@ a["beta"] = 6;
|
||||
print a;
|
||||
assert a == ["alpha": 1, "beta": 6, "gamma": 3], "1-D tables failed";
|
||||
|
||||
//WONTFIX: Nested tables cause an issue under very specific circumstances: run under GDB, in verbose mode, only in github's CI runner. The reason is unclear.
|
||||
|
||||
//nested
|
||||
var b = [
|
||||
"outer": ["inner": true],
|
||||
|
||||
@@ -157,4 +157,4 @@ print !false; //true
|
||||
print a;
|
||||
}
|
||||
|
||||
//TODO: type casting
|
||||
//TODO: type casting
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
|
||||
int main(void) {
|
||||
//TODO: Test not yet implemented
|
||||
printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__);
|
||||
return 0;
|
||||
}
|
||||
@@ -117,6 +117,7 @@ int test_functions_from_bytecodes(void) {
|
||||
}
|
||||
|
||||
int test_functions_from_callbacks(void) {
|
||||
//TODO: Test not yet implemented
|
||||
printf(TOY_CC_WARN "WIP test not yet implemented: %s\n" TOY_CC_RESET, __FILE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ int test_string_concatenation(void) {
|
||||
|
||||
free(buffer);
|
||||
Toy_freeString(result);
|
||||
Toy_freeString(first);
|
||||
Toy_freeString(second);
|
||||
// Toy_freeString(first); //these do NOT need to be freed manually
|
||||
// Toy_freeString(second);
|
||||
Toy_freeBucket(&bucket);
|
||||
}
|
||||
|
||||
|
||||
@@ -609,6 +609,7 @@ int main(void) {
|
||||
|
||||
//TODO: references
|
||||
//TODO: type coersions
|
||||
//TODO: opaques?
|
||||
|
||||
return total;
|
||||
}
|
||||
Reference in New Issue
Block a user