mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Tweaked page names
This commit is contained in:
@@ -50,13 +50,13 @@ print tally(); //3
|
|||||||
|
|
||||||
# Full C API
|
# Full C API
|
||||||
|
|
||||||
* Coming Soon! (check out the [Using Toy](using-toy) page for a brief description for now)
|
* Coming Soon! (check out the [Embedding Toy](embedding-toy) page for a brief description for now)
|
||||||
|
|
||||||
# Deep Dive
|
# Deep Dive
|
||||||
|
|
||||||
* [Theorizing Toy](theorizing-toy)
|
* [Theorizing Toy](theorizing-toy)
|
||||||
|
* [Building Toy](building-toy)
|
||||||
* [Embedding Toy](embedding-toy)
|
* [Embedding Toy](embedding-toy)
|
||||||
* [Using Toy](using-toy)
|
|
||||||
* [Compiling Toy](compiling-toy)
|
* [Compiling Toy](compiling-toy)
|
||||||
* [Developing Toy](developing-toy)
|
* [Developing Toy](developing-toy)
|
||||||
* [Testing Toy](testing-toy)
|
* [Testing Toy](testing-toy)
|
||||||
|
|||||||
68
building-toy.md
Normal file
68
building-toy.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Building Toy
|
||||||
|
|
||||||
|
This tutorial assumes you're using git, GCC, and make.
|
||||||
|
|
||||||
|
To embed toy into your program, simply clone the [git repository](https://github.com/Ratstail91/Toy) into a submodule - here we'll assume you called it `Toy`.
|
||||||
|
|
||||||
|
Toy's makefile uses the variable `TOY_OUTDIR` to define where the output of the build command will place the result. You MUST set this to a value, relative to the Toy directory.
|
||||||
|
|
||||||
|
```make
|
||||||
|
export LIBDIR = lib
|
||||||
|
export TOY_OUTDIR = ../$(LIBDIR)
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, you'll want to run make the from within Toy's `source`, assuming the output directory has been created. There are two options for building Toy - `library` (default) or `static`; the former will create a shared library (and a .dll file on windows), while the latter will create a static library.
|
||||||
|
|
||||||
|
```make
|
||||||
|
toy: $(LIBDIR)
|
||||||
|
$(MAKE) -C Toy/source
|
||||||
|
|
||||||
|
$(LIBDIR):
|
||||||
|
mkdir $(LIBDIR)
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, link to the outputted library, and specify the source directory to access the header files.
|
||||||
|
|
||||||
|
```make
|
||||||
|
all: $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) -o $(OUT) $(OBJ) -L../$(LIBDIR) $(LIBS)
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's a quick example makefile template you can use:
|
||||||
|
|
||||||
|
```make
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
|
export OUTDIR = out
|
||||||
|
export LIBDIR = lib
|
||||||
|
export TOY_OUTDIR = ../$(LIBDIR)
|
||||||
|
|
||||||
|
IDIR+=. ./Toy/source
|
||||||
|
CFLAGS+=$(addprefix -I,$(IDIR))
|
||||||
|
LIBS+=-ltoy
|
||||||
|
|
||||||
|
ODIR=obj
|
||||||
|
SRC=$(wildcard *.c)
|
||||||
|
OBJ=$(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||||
|
|
||||||
|
OUT=./$(OUTDIR)/program
|
||||||
|
|
||||||
|
all: toy $(OUTDIR) $(ODIR) $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) -o $(OUT) $(OBJ) -L$(LIBDIR) $(LIBS)
|
||||||
|
cp $(LIBDIR)/*.dll $(OUTDIR) # for shared libraries
|
||||||
|
|
||||||
|
toy: $(LIBDIR)
|
||||||
|
$(MAKE) -C Toy/source
|
||||||
|
|
||||||
|
$(OUTDIR):
|
||||||
|
mkdir $(OUTDIR)
|
||||||
|
|
||||||
|
$(LIBDIR):
|
||||||
|
mkdir $(LIBDIR)
|
||||||
|
|
||||||
|
$(ODIR):
|
||||||
|
mkdir $(ODIR)
|
||||||
|
|
||||||
|
$(ODIR)/%.o: %.c
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
```
|
||||||
151
embedding-toy.md
151
embedding-toy.md
@@ -1,68 +1,119 @@
|
|||||||
# Embedding Toy
|
# Embedding Toy
|
||||||
|
|
||||||
This tutorial assumes you're using git, GCC, and make.
|
This tutorial assumes that you've managed to embed Toy into your program by following the tutorial [Using Toy](using-toy).
|
||||||
|
|
||||||
To embed toy into your program, simply clone the [git repository](https://github.com/Ratstail91/Toy) into a submodule - here we'll assume you called it `Toy`.
|
Here, we'll look at some ways in which you can utilize Toy's C API within your host program.
|
||||||
|
|
||||||
Toy's makefile uses the variable `TOY_OUTDIR` to define where the output of the build command will place the result. You MUST set this to a value, relative to the Toy directory.
|
Be aware that when you create a new Literal object, you must call `Toy_freeLiteral()` on it afterwards! If you don't, your program will leak memory as Toy has no internal tracker for such things.
|
||||||
|
|
||||||
```make
|
## Embedded API Macros
|
||||||
export LIBDIR = lib
|
|
||||||
export TOY_OUTDIR = ../$(LIBDIR)
|
The functions intended for usage by the API are prepended with the C macro `TOY_API`. The exact value of this macro can vary by platform, or even be empty. In addition, the macros defined in [literal.h](https://github.com/Ratstail91/Toy/blob/0.6.0/source/literal.h) are available for use when manipulating literals. These include:
|
||||||
|
|
||||||
|
* `TOY_IS_*` - check if a literal is a specific type
|
||||||
|
* `TOY_AS_*` - cast the literal to a specific type
|
||||||
|
* `TOY_TO_*` - create a literal of a specific type
|
||||||
|
* `TOY_IS_TRUTHY` - check if a literal is truthy
|
||||||
|
* `TOY_MAX_STRING_LENGTH` - the maximum length of a string in Toy (can be altered if needed)
|
||||||
|
|
||||||
|
## Structures Used Throughout Toy
|
||||||
|
|
||||||
|
The main unit of data within Toy's internals is `Toy_Literal`, which can contain any value that can exist within the Toy langauge. The exact implementation of `Toy_Literal` may change or evolve as time goes on, so it's recommended that you only interact with literals directly by using the macros and functions outlined [above](#embedded-api-macros). See the [types](types) page for information on what datatypes exist in Toy.
|
||||||
|
|
||||||
|
There are two main "compound structures" used within Toy's internals - the `Toy_LiteralArray` and `Toy_LiteralDictionary`. The former is an array of `Toy_Literal` instances stored sequentially in memory for fast lookups, while the latter is a key-value hashmap designed for efficient lookups based on a `Toy_Literal` key. These are both accessible via the language as well.
|
||||||
|
|
||||||
|
These compound structures hold **copies** of literals given to them, rather than taking ownership of existing literals.
|
||||||
|
|
||||||
|
## Compiling Toy Scripts
|
||||||
|
|
||||||
|
Please see [Compiling Toy](compiling-toy) for more information on the process of turning scripts into bytecode.
|
||||||
|
|
||||||
|
## Interpreting Toy
|
||||||
|
|
||||||
|
The `Toy_Interpreter` structure is the beating heart of Toy - You'll usually only need one interpreter, as it can be reset as needed.
|
||||||
|
|
||||||
|
The four basic functions are used as follows:
|
||||||
|
|
||||||
|
```c
|
||||||
|
//assume "tb" and "size" are the results of compilation
|
||||||
|
Toy_Interpreter interpreter;
|
||||||
|
|
||||||
|
Toy_initInterpreter(&interpreter);
|
||||||
|
Toy_runInterpreter(&interpreter, tb, size);
|
||||||
|
Toy_resetInterpreter(&interpreter); //You usually want to reset between runs
|
||||||
|
Toy_freeInterpreter(&interpreter);
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, you'll want to run make the from within Toy's `source`, assuming the output directory has been created. There are two options for building Toy - `library` (default) or `static`; the former will create a shared library (and a .dll file on windows), while the latter will create a static library.
|
In addition to this, you might also wish to "inject" a series of usable libraries into the interpreter, which can be `import`-ed within the language itself. This process only needs to be done once, after initialization, but before the first run.
|
||||||
|
|
||||||
```make
|
```c
|
||||||
toy: $(LIBDIR)
|
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
||||||
$(MAKE) -C Toy/source
|
|
||||||
|
|
||||||
$(LIBDIR):
|
|
||||||
mkdir $(LIBDIR)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, link to the outputted library, and specify the source directory to access the header files.
|
A "hook" is a callback function which is invoked when the given library is imported. `standard` is the most commonly used library available.
|
||||||
|
|
||||||
```make
|
```
|
||||||
all: $(OBJ)
|
import standard;
|
||||||
$(CC) $(CFLAGS) -o $(OUT) $(OBJ) -L../$(LIBDIR) $(LIBS)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's a quick example makefile template you can use:
|
Hooks can simply inject native functions into the current scope, or they can do other, more esoteric things (though this is not recommended).
|
||||||
|
|
||||||
```make
|
```c
|
||||||
CC=gcc
|
//a utility structure for storing the native C functions
|
||||||
|
typedef struct Natives {
|
||||||
|
char* name;
|
||||||
|
Toy_NativeFn fn;
|
||||||
|
} Natives;
|
||||||
|
|
||||||
export OUTDIR = out
|
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
||||||
export LIBDIR = lib
|
//the list of available native C functions that can be called from Toy
|
||||||
export TOY_OUTDIR = ../$(LIBDIR)
|
Natives natives[] = {
|
||||||
|
{"clock", nativeClock},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
IDIR+=. ./Toy/source
|
//inject each native C functions into the current scope
|
||||||
CFLAGS+=$(addprefix -I,$(IDIR))
|
for (int i = 0; natives[i].name; i++) {
|
||||||
LIBS+=-ltoy
|
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
||||||
|
}
|
||||||
|
|
||||||
ODIR=obj
|
return 0;
|
||||||
SRC=$(wildcard *.c)
|
}
|
||||||
OBJ=$(addprefix $(ODIR)/,$(SRC:.c=.o))
|
|
||||||
|
|
||||||
OUT=./$(OUTDIR)/program
|
|
||||||
|
|
||||||
all: toy $(OUTDIR) $(ODIR) $(OBJ)
|
|
||||||
$(CC) $(CFLAGS) -o $(OUT) $(OBJ) -L$(LIBDIR) $(LIBS)
|
|
||||||
cp $(LIBDIR)/*.dll $(OUTDIR) # for shared libraries
|
|
||||||
|
|
||||||
toy: $(LIBDIR)
|
|
||||||
$(MAKE) -C Toy/source
|
|
||||||
|
|
||||||
$(OUTDIR):
|
|
||||||
mkdir $(OUTDIR)
|
|
||||||
|
|
||||||
$(LIBDIR):
|
|
||||||
mkdir $(LIBDIR)
|
|
||||||
|
|
||||||
$(ODIR):
|
|
||||||
mkdir $(ODIR)
|
|
||||||
|
|
||||||
$(ODIR)/%.o: %.c
|
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Calling Toy from C
|
||||||
|
|
||||||
|
In some situations, you may find it convenient to call a function written in Toy from the host program. For this, a pair of utility functions have been provided:
|
||||||
|
|
||||||
|
```c
|
||||||
|
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||||
|
TOY_API bool Toy_callFn (Toy_Interpreter* interpreter, char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
||||||
|
```
|
||||||
|
|
||||||
|
The first argument must be an interpreter. The third argument is a pointer to a `Toy_LiteralArray` containing a list of arguments to pass to the function, and the fourth is a pointer to a `Toy_LiteralArray` where the return values can be stored (an array is used here for a potential future feature). The contents of the argument array is consumed and left in an indeterminate state (but is safe to free), while the returns array always has one value - if the function did not return a value, then it contains a `null` literal.
|
||||||
|
|
||||||
|
The second arguments to these functions are either the function to be called as a `Toy_Literal`, or the name of the function within the interpreter's scope. The latter API simply finds the specified `Toy_Literal` if it exists and calls the former. As with most APIs, these return `false` if something went wrong.
|
||||||
|
|
||||||
|
## Memory Allocation
|
||||||
|
|
||||||
|
Depending on your platform of choice, you may want to alter how the memory is allocated within Toy. You can do this with the simple memory API:
|
||||||
|
|
||||||
|
```c
|
||||||
|
//signature returns the new pointer to be used
|
||||||
|
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass it a function which matches the above signature, and it'll be callable via the following macros:
|
||||||
|
|
||||||
|
* `TOY_ALLOCATE(type, count)`
|
||||||
|
* `TOY_FREE(type, pointer)`
|
||||||
|
* `TOY_GROW_ARRAY(type, pointer, oldCount, newCount)`
|
||||||
|
* `TOY_SHRINK_ARRAY(type, pointer, oldCount, newCount)`
|
||||||
|
* `TOY_FREE_ARRAY(type, pointer, oldCount)`
|
||||||
|
|
||||||
|
Also, the following macros are provided to calculate the ideal array capacities (the latter of which is for rapidly growing structures):
|
||||||
|
|
||||||
|
* `TOY_GROW_CAPACITY(capacity)`
|
||||||
|
* `TOY_GROW_CAPACITY_FAST(capacity)`
|
||||||
|
|
||||||
|
|||||||
119
using-toy.md
119
using-toy.md
@@ -1,119 +0,0 @@
|
|||||||
# Using Toy
|
|
||||||
|
|
||||||
This tutorial assumes that you've managed to embed Toy into your program by following the tutorial [Embedding Toy](embedding-toy).
|
|
||||||
|
|
||||||
Here, we'll look at some ways in which you can utilize Toy's C API within your host program.
|
|
||||||
|
|
||||||
Be aware that when you create a new Literal object, you must call `Toy_freeLiteral()` on it afterwards! If you don't, your program will leak memory as Toy has no internal tracker for such things.
|
|
||||||
|
|
||||||
## Embedded API Macros
|
|
||||||
|
|
||||||
The functions intended for usage by the API are prepended with the C macro `TOY_API`. The exact value of this macro can vary by platform, or even be empty. In addition, the macros defined in [literal.h](https://github.com/Ratstail91/Toy/blob/0.6.0/source/literal.h) are available for use when manipulating literals. These include:
|
|
||||||
|
|
||||||
* `TOY_IS_*` - check if a literal is a specific type
|
|
||||||
* `TOY_AS_*` - cast the literal to a specific type
|
|
||||||
* `TOY_TO_*` - create a literal of a specific type
|
|
||||||
* `TOY_IS_TRUTHY` - check if a literal is truthy
|
|
||||||
* `TOY_MAX_STRING_LENGTH` - the maximum length of a string in Toy (can be altered if needed)
|
|
||||||
|
|
||||||
## Structures Used Throughout Toy
|
|
||||||
|
|
||||||
The main unit of data within Toy's internals is `Toy_Literal`, which can contain any value that can exist within the Toy langauge. The exact implementation of `Toy_Literal` may change or evolve as time goes on, so it's recommended that you only interact with literals directly by using the macros and functions outlined [above](#embedded-api-macros). See the [types](types) page for information on what datatypes exist in Toy.
|
|
||||||
|
|
||||||
There are two main "compound structures" used within Toy's internals - the `Toy_LiteralArray` and `Toy_LiteralDictionary`. The former is an array of `Toy_Literal` instances stored sequentially in memory for fast lookups, while the latter is a key-value hashmap designed for efficient lookups based on a `Toy_Literal` key. These are both accessible via the language as well.
|
|
||||||
|
|
||||||
These compound structures hold **copies** of literals given to them, rather than taking ownership of existing literals.
|
|
||||||
|
|
||||||
## Compiling Toy Scripts
|
|
||||||
|
|
||||||
Please see [Compiling Toy](compiling-toy) for more information on the process of turning scripts into bytecode.
|
|
||||||
|
|
||||||
## Interpreting Toy
|
|
||||||
|
|
||||||
The `Toy_Interpreter` structure is the beating heart of Toy - You'll usually only need one interpreter, as it can be reset as needed.
|
|
||||||
|
|
||||||
The four basic functions are used as follows:
|
|
||||||
|
|
||||||
```c
|
|
||||||
//assume "tb" and "size" are the results of compilation
|
|
||||||
Toy_Interpreter interpreter;
|
|
||||||
|
|
||||||
Toy_initInterpreter(&interpreter);
|
|
||||||
Toy_runInterpreter(&interpreter, tb, size);
|
|
||||||
Toy_resetInterpreter(&interpreter); //You usually want to reset between runs
|
|
||||||
Toy_freeInterpreter(&interpreter);
|
|
||||||
```
|
|
||||||
|
|
||||||
In addition to this, you might also wish to "inject" a series of usable libraries into the interpreter, which can be `import`-ed within the language itself. This process only needs to be done once, after initialization, but before the first run.
|
|
||||||
|
|
||||||
```c
|
|
||||||
Toy_injectNativeHook(&interpreter, "standard", Toy_hookStandard);
|
|
||||||
```
|
|
||||||
|
|
||||||
A "hook" is a callback function which is invoked when the given library is imported. `standard` is the most commonly used library available.
|
|
||||||
|
|
||||||
```
|
|
||||||
import standard;
|
|
||||||
```
|
|
||||||
|
|
||||||
Hooks can simply inject native functions into the current scope, or they can do other, more esoteric things (though this is not recommended).
|
|
||||||
|
|
||||||
```c
|
|
||||||
//a utility structure for storing the native C functions
|
|
||||||
typedef struct Natives {
|
|
||||||
char* name;
|
|
||||||
Toy_NativeFn fn;
|
|
||||||
} Natives;
|
|
||||||
|
|
||||||
int Toy_hookStandard(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Literal alias) {
|
|
||||||
//the list of available native C functions that can be called from Toy
|
|
||||||
Natives natives[] = {
|
|
||||||
{"clock", nativeClock},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
//inject each native C functions into the current scope
|
|
||||||
for (int i = 0; natives[i].name; i++) {
|
|
||||||
Toy_injectNativeFn(interpreter, natives[i].name, natives[i].fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Calling Toy from C
|
|
||||||
|
|
||||||
In some situations, you may find it convenient to call a function written in Toy from the host program. For this, a pair of utility functions have been provided:
|
|
||||||
|
|
||||||
```c
|
|
||||||
TOY_API bool Toy_callLiteralFn(Toy_Interpreter* interpreter, Toy_Literal func, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
|
||||||
TOY_API bool Toy_callFn (Toy_Interpreter* interpreter, char* name, Toy_LiteralArray* arguments, Toy_LiteralArray* returns);
|
|
||||||
```
|
|
||||||
|
|
||||||
The first argument must be an interpreter. The third argument is a pointer to a `Toy_LiteralArray` containing a list of arguments to pass to the function, and the fourth is a pointer to a `Toy_LiteralArray` where the return values can be stored (an array is used here for a potential future feature). The contents of the argument array is consumed and left in an indeterminate state (but is safe to free), while the returns array always has one value - if the function did not return a value, then it contains a `null` literal.
|
|
||||||
|
|
||||||
The second arguments to these functions are either the function to be called as a `Toy_Literal`, or the name of the function within the interpreter's scope. The latter API simply finds the specified `Toy_Literal` if it exists and calls the former. As with most APIs, these return `false` if something went wrong.
|
|
||||||
|
|
||||||
## Memory Allocation
|
|
||||||
|
|
||||||
Depending on your platform of choice, you may want to alter how the memory is allocated within Toy. You can do this with the simple memory API:
|
|
||||||
|
|
||||||
```c
|
|
||||||
//signature returns the new pointer to be used
|
|
||||||
typedef void* (*Toy_MemoryAllocatorFn)(void* pointer, size_t oldSize, size_t newSize);
|
|
||||||
TOY_API void Toy_setMemoryAllocator(Toy_MemoryAllocatorFn);
|
|
||||||
```
|
|
||||||
|
|
||||||
Pass it a function which matches the above signature, and it'll be callable via the following macros:
|
|
||||||
|
|
||||||
* `TOY_ALLOCATE(type, count)`
|
|
||||||
* `TOY_FREE(type, pointer)`
|
|
||||||
* `TOY_GROW_ARRAY(type, pointer, oldCount, newCount)`
|
|
||||||
* `TOY_SHRINK_ARRAY(type, pointer, oldCount, newCount)`
|
|
||||||
* `TOY_FREE_ARRAY(type, pointer, oldCount)`
|
|
||||||
|
|
||||||
Also, the following macros are provided to calculate the ideal array capacities (the latter of which is for rapidly growing structures):
|
|
||||||
|
|
||||||
* `TOY_GROW_CAPACITY(capacity)`
|
|
||||||
* `TOY_GROW_CAPACITY_FAST(capacity)`
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user