docs revamp
This commit is contained in:
16
docs/.pages
16
docs/.pages
@@ -1,13 +1,9 @@
|
||||
nav:
|
||||
- index.md
|
||||
- quickstart.md
|
||||
- tutorial.md
|
||||
- cellscript.md
|
||||
- actors.md
|
||||
- rendering.md
|
||||
- resources.md
|
||||
- input.md
|
||||
- exporting.md
|
||||
- ...
|
||||
- Appendix A - dull: dull
|
||||
- Appendix B - api: api
|
||||
|
||||
- packages.md
|
||||
- cli.md
|
||||
- c-modules.md
|
||||
- Standard Library: library
|
||||
|
||||
|
||||
230
docs/actors.md
Normal file
230
docs/actors.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# Actors and Modules
|
||||
|
||||
Cell organizes code into two types of scripts: **modules** (`.cm`) and **actors** (`.ce`).
|
||||
|
||||
## The Actor Model
|
||||
|
||||
Cell is built on the actor model of computation. Each actor:
|
||||
|
||||
- Has its own **isolated memory** — actors never share state
|
||||
- Runs to completion each **turn** — no preemption
|
||||
- Performs its own **garbage collection**
|
||||
- Communicates only through **message passing**
|
||||
|
||||
This isolation makes concurrent programming safer and more predictable.
|
||||
|
||||
## Modules (.cm)
|
||||
|
||||
A module is a script that **returns a value**. The returned value is cached and frozen (made stone).
|
||||
|
||||
```javascript
|
||||
// math_utils.cm
|
||||
var math = use('math/radians')
|
||||
|
||||
function distance(x1, y1, x2, y2) {
|
||||
var dx = x2 - x1
|
||||
var dy = y2 - y1
|
||||
return math.sqrt(dx * dx + dy * dy)
|
||||
}
|
||||
|
||||
function midpoint(x1, y1, x2, y2) {
|
||||
return {
|
||||
x: (x1 + x2) / 2,
|
||||
y: (y1 + y2) / 2
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
distance: distance,
|
||||
midpoint: midpoint
|
||||
}
|
||||
```
|
||||
|
||||
**Key properties:**
|
||||
|
||||
- **Must return a value** — it's an error not to
|
||||
- **Executed once per actor** — subsequent `use()` calls return the cached value
|
||||
- **Return value is stone** — immutable, safe to share
|
||||
- Modules can import other modules with `use()`
|
||||
|
||||
### Using Modules
|
||||
|
||||
```javascript
|
||||
var utils = use('math_utils')
|
||||
var d = utils.distance(0, 0, 3, 4) // 5
|
||||
```
|
||||
|
||||
## Actors (.ce)
|
||||
|
||||
An actor is a script that **does not return a value**. It runs as an independent unit of execution.
|
||||
|
||||
```javascript
|
||||
// worker.ce
|
||||
log.console("Worker started")
|
||||
|
||||
$_.on_message = function(msg) {
|
||||
log.console("Received:", msg)
|
||||
// Process message...
|
||||
}
|
||||
```
|
||||
|
||||
**Key properties:**
|
||||
|
||||
- **Must not return a value** — it's an error to return
|
||||
- Has access to **actor intrinsics** (functions starting with `$`)
|
||||
- Runs until explicitly stopped or crashes
|
||||
|
||||
## Actor Intrinsics
|
||||
|
||||
Actors have access to special functions prefixed with `$`:
|
||||
|
||||
### $me
|
||||
|
||||
Reference to the current actor.
|
||||
|
||||
```javascript
|
||||
log.console($me) // actor reference
|
||||
```
|
||||
|
||||
### $_.stop()
|
||||
|
||||
Stop the current actor.
|
||||
|
||||
```javascript
|
||||
$_.stop()
|
||||
```
|
||||
|
||||
### $send(actor, message, callback)
|
||||
|
||||
Send a message to another actor.
|
||||
|
||||
```javascript
|
||||
$send(other_actor, {type: "ping", data: 42}, function(reply) {
|
||||
log.console("Got reply:", reply)
|
||||
})
|
||||
```
|
||||
|
||||
Messages are automatically **splatted** — flattened to plain data without prototypes.
|
||||
|
||||
### $start(callback, program)
|
||||
|
||||
Start a new actor from a script.
|
||||
|
||||
```javascript
|
||||
$start(function(new_actor) {
|
||||
log.console("Started:", new_actor)
|
||||
}, "worker")
|
||||
```
|
||||
|
||||
### $delay(callback, seconds)
|
||||
|
||||
Schedule a callback after a delay.
|
||||
|
||||
```javascript
|
||||
$delay(function() {
|
||||
log.console("5 seconds later")
|
||||
}, 5)
|
||||
```
|
||||
|
||||
### $clock(callback)
|
||||
|
||||
Get called every frame/tick.
|
||||
|
||||
```javascript
|
||||
$clock(function(dt) {
|
||||
// Called each tick with delta time
|
||||
})
|
||||
```
|
||||
|
||||
### $receiver(callback)
|
||||
|
||||
Set up a message receiver.
|
||||
|
||||
```javascript
|
||||
$receiver(function(message, reply) {
|
||||
// Handle incoming message
|
||||
reply({status: "ok"})
|
||||
})
|
||||
```
|
||||
|
||||
### $portal(callback, port)
|
||||
|
||||
Open a network port.
|
||||
|
||||
```javascript
|
||||
$portal(function(connection) {
|
||||
// Handle new connection
|
||||
}, 8080)
|
||||
```
|
||||
|
||||
### $contact(callback, record)
|
||||
|
||||
Connect to a remote address.
|
||||
|
||||
```javascript
|
||||
$contact(function(connection) {
|
||||
// Connected
|
||||
}, {host: "example.com", port: 80})
|
||||
```
|
||||
|
||||
### $time_limit(requestor, seconds)
|
||||
|
||||
Wrap a requestor with a timeout.
|
||||
|
||||
```javascript
|
||||
$time_limit(my_requestor, 10) // 10 second timeout
|
||||
```
|
||||
|
||||
## Module Resolution
|
||||
|
||||
When you call `use('name')`, Cell searches:
|
||||
|
||||
1. **Current package** — files relative to package root
|
||||
2. **Dependencies** — packages declared in `cell.toml`
|
||||
3. **Core** — built-in Cell modules
|
||||
|
||||
```javascript
|
||||
// From within package 'myapp':
|
||||
use('utils') // myapp/utils.cm
|
||||
use('helper/math') // myapp/helper/math.cm
|
||||
use('json') // core json module
|
||||
use('otherlib/foo') // dependency 'otherlib', file foo.cm
|
||||
```
|
||||
|
||||
Files starting with underscore (`_helper.cm`) are private to the package.
|
||||
|
||||
## Example: Simple Actor System
|
||||
|
||||
```javascript
|
||||
// main.ce - Entry point
|
||||
var config = use('config')
|
||||
|
||||
log.console("Starting application...")
|
||||
|
||||
$start(function(worker) {
|
||||
$send(worker, {task: "process", data: [1, 2, 3]})
|
||||
}, "worker")
|
||||
|
||||
$delay(function() {
|
||||
log.console("Shutting down")
|
||||
$_.stop()
|
||||
}, 10)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// worker.ce - Worker actor
|
||||
$receiver(function(msg, reply) {
|
||||
if (msg.task == "process") {
|
||||
var result = array(msg.data, x => x * 2)
|
||||
reply({result: result})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// config.cm - Shared configuration
|
||||
return {
|
||||
debug: true,
|
||||
timeout: 30
|
||||
}
|
||||
```
|
||||
277
docs/c-modules.md
Normal file
277
docs/c-modules.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Writing C Modules
|
||||
|
||||
Cell makes it easy to extend functionality with C code. C files in a package are compiled into a dynamic library and can be imported like any other module.
|
||||
|
||||
## Basic Structure
|
||||
|
||||
A C module exports a single function that returns a JavaScript value:
|
||||
|
||||
```c
|
||||
// mymodule.c
|
||||
#include "cell.h"
|
||||
|
||||
#define CELL_USE_NAME js_mypackage_mymodule_use
|
||||
|
||||
static JSValue js_add(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
double a = js2number(js, argv[0]);
|
||||
double b = js2number(js, argv[1]);
|
||||
return number2js(js, a + b);
|
||||
}
|
||||
|
||||
static JSValue js_multiply(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
double a = js2number(js, argv[0]);
|
||||
double b = js2number(js, argv[1]);
|
||||
return number2js(js, a * b);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_funcs[] = {
|
||||
MIST_FUNC_DEF(mymodule, add, 2),
|
||||
MIST_FUNC_DEF(mymodule, multiply, 2),
|
||||
};
|
||||
|
||||
CELL_USE_FUNCS(js_funcs)
|
||||
```
|
||||
|
||||
## Symbol Naming
|
||||
|
||||
The exported function must follow this naming convention:
|
||||
|
||||
```
|
||||
js_<package>_<filename>_use
|
||||
```
|
||||
|
||||
Where:
|
||||
- `<package>` is the package name with `/` and `.` replaced by `_`
|
||||
- `<filename>` is the C file name without extension
|
||||
|
||||
Examples:
|
||||
- `mypackage/math.c` → `js_mypackage_math_use`
|
||||
- `gitea.pockle.world/john/lib/render.c` → `js_gitea_pockle_world_john_lib_render_use`
|
||||
|
||||
## Required Headers
|
||||
|
||||
Include `cell.h` for all Cell integration:
|
||||
|
||||
```c
|
||||
#include "cell.h"
|
||||
```
|
||||
|
||||
This provides:
|
||||
- QuickJS types and functions
|
||||
- Conversion helpers
|
||||
- Module definition macros
|
||||
|
||||
## Conversion Functions
|
||||
|
||||
### JavaScript ↔ C
|
||||
|
||||
```c
|
||||
// Numbers
|
||||
double js2number(JSContext *js, JSValue v);
|
||||
JSValue number2js(JSContext *js, double g);
|
||||
|
||||
// Booleans
|
||||
int js2bool(JSContext *js, JSValue v);
|
||||
JSValue bool2js(JSContext *js, int b);
|
||||
|
||||
// Strings (must free with JS_FreeCString)
|
||||
const char *JS_ToCString(JSContext *js, JSValue v);
|
||||
void JS_FreeCString(JSContext *js, const char *str);
|
||||
JSValue JS_NewString(JSContext *js, const char *str);
|
||||
```
|
||||
|
||||
### Blobs
|
||||
|
||||
```c
|
||||
// Get blob data (returns pointer, sets size in bytes)
|
||||
void *js_get_blob_data(JSContext *js, size_t *size, JSValue v);
|
||||
|
||||
// Get blob data in bits
|
||||
void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v);
|
||||
|
||||
// Create new stone blob from data
|
||||
JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes);
|
||||
|
||||
// Check if value is a blob
|
||||
int js_is_blob(JSContext *js, JSValue v);
|
||||
```
|
||||
|
||||
## Function Definition Macros
|
||||
|
||||
### JSC_CCALL
|
||||
|
||||
Define a function with automatic return value:
|
||||
|
||||
```c
|
||||
JSC_CCALL(mymodule_greet,
|
||||
const char *name = JS_ToCString(js, argv[0]);
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Hello, %s!", name);
|
||||
ret = JS_NewString(js, buf);
|
||||
JS_FreeCString(js, name);
|
||||
)
|
||||
```
|
||||
|
||||
### JSC_SCALL
|
||||
|
||||
Shorthand for functions taking a string first argument:
|
||||
|
||||
```c
|
||||
JSC_SCALL(mymodule_strlen,
|
||||
ret = number2js(js, strlen(str));
|
||||
)
|
||||
```
|
||||
|
||||
### MIST_FUNC_DEF
|
||||
|
||||
Register a function in the function list:
|
||||
|
||||
```c
|
||||
MIST_FUNC_DEF(prefix, function_name, arg_count)
|
||||
```
|
||||
|
||||
## Module Export Macros
|
||||
|
||||
### CELL_USE_FUNCS
|
||||
|
||||
Export an object with functions:
|
||||
|
||||
```c
|
||||
static const JSCFunctionListEntry js_funcs[] = {
|
||||
MIST_FUNC_DEF(mymod, func1, 1),
|
||||
MIST_FUNC_DEF(mymod, func2, 2),
|
||||
};
|
||||
|
||||
CELL_USE_FUNCS(js_funcs)
|
||||
```
|
||||
|
||||
### CELL_USE_INIT
|
||||
|
||||
For custom initialization:
|
||||
|
||||
```c
|
||||
CELL_USE_INIT(
|
||||
JSValue obj = JS_NewObject(js);
|
||||
// Custom setup...
|
||||
return obj;
|
||||
)
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```c
|
||||
// vector.c - Simple 2D vector operations
|
||||
#include "cell.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CELL_USE_NAME js_mypackage_vector_use
|
||||
|
||||
JSC_CCALL(vector_length,
|
||||
double x = js2number(js, argv[0]);
|
||||
double y = js2number(js, argv[1]);
|
||||
ret = number2js(js, sqrt(x*x + y*y));
|
||||
)
|
||||
|
||||
JSC_CCALL(vector_normalize,
|
||||
double x = js2number(js, argv[0]);
|
||||
double y = js2number(js, argv[1]);
|
||||
double len = sqrt(x*x + y*y);
|
||||
if (len > 0) {
|
||||
JSValue result = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, result, "x", number2js(js, x/len));
|
||||
JS_SetPropertyStr(js, result, "y", number2js(js, y/len));
|
||||
ret = result;
|
||||
}
|
||||
)
|
||||
|
||||
JSC_CCALL(vector_dot,
|
||||
double x1 = js2number(js, argv[0]);
|
||||
double y1 = js2number(js, argv[1]);
|
||||
double x2 = js2number(js, argv[2]);
|
||||
double y2 = js2number(js, argv[3]);
|
||||
ret = number2js(js, x1*x2 + y1*y2);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_funcs[] = {
|
||||
MIST_FUNC_DEF(vector, length, 2),
|
||||
MIST_FUNC_DEF(vector, normalize, 2),
|
||||
MIST_FUNC_DEF(vector, dot, 4),
|
||||
};
|
||||
|
||||
CELL_USE_FUNCS(js_funcs)
|
||||
```
|
||||
|
||||
Usage in Cell:
|
||||
|
||||
```javascript
|
||||
var vector = use('vector')
|
||||
|
||||
var len = vector.length(3, 4) // 5
|
||||
var n = vector.normalize(3, 4) // {x: 0.6, y: 0.8}
|
||||
var d = vector.dot(1, 0, 0, 1) // 0
|
||||
```
|
||||
|
||||
## Combining C and Cell
|
||||
|
||||
A common pattern is to have a C file provide low-level functions and a `.cm` file provide a higher-level API:
|
||||
|
||||
```c
|
||||
// _vector_native.c
|
||||
// ... raw C functions ...
|
||||
```
|
||||
|
||||
```javascript
|
||||
// vector.cm
|
||||
var native = this // C module passed as 'this'
|
||||
|
||||
function Vector(x, y) {
|
||||
return {x: x, y: y}
|
||||
}
|
||||
|
||||
Vector.length = function(v) {
|
||||
return native.length(v.x, v.y)
|
||||
}
|
||||
|
||||
Vector.normalize = function(v) {
|
||||
return native.normalize(v.x, v.y)
|
||||
}
|
||||
|
||||
return Vector
|
||||
```
|
||||
|
||||
## Build Process
|
||||
|
||||
C files are automatically compiled when you run:
|
||||
|
||||
```bash
|
||||
cell build
|
||||
cell update
|
||||
```
|
||||
|
||||
The resulting dynamic library is placed in `~/.cell/lib/`.
|
||||
|
||||
## Platform-Specific Code
|
||||
|
||||
Use filename suffixes for platform variants:
|
||||
|
||||
```
|
||||
audio.c # default
|
||||
audio_playdate.c # Playdate
|
||||
audio_emscripten.c # Web/Emscripten
|
||||
```
|
||||
|
||||
Cell selects the appropriate file based on the target platform.
|
||||
|
||||
## Static Declarations
|
||||
|
||||
Keep internal functions and variables `static`:
|
||||
|
||||
```c
|
||||
static int helper_function(int x) {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
static int module_state = 0;
|
||||
```
|
||||
|
||||
This prevents symbol conflicts between packages.
|
||||
@@ -1,626 +1,288 @@
|
||||
# Cell Language
|
||||
|
||||
Cell is a JavaScript variant used in cell. While very similar to JavaScript, it has several important differences that make it more suitable for actor-based programming.
|
||||
Cell is a scripting language for actor-based programming. It combines a familiar syntax with a prototype-based object system and strict immutability semantics.
|
||||
|
||||
Variables are delcared with 'var'. Var behaves like let.
|
||||
Constants are declared with 'def'.
|
||||
!= and == are strict, there is no !== or ===.
|
||||
There is no undefined, only null.
|
||||
There are no classes, only objects and prototypes.
|
||||
There are no arraybuffers.
|
||||
There are no automatic conversions for types.
|
||||
No proxy.
|
||||
No JS modules.
|
||||
No bigint.
|
||||
No reflect.
|
||||
No weakmap, weakset, weakref.
|
||||
No with.
|
||||
No Number, Date, Array, Math, Function
|
||||
No setters or getters or any other property descriptor things
|
||||
No eval.
|
||||
No new keyword.
|
||||
No instanceof.
|
||||
## Basics
|
||||
|
||||
The point of cell is to merge the prototype system and actor systems, by giving ergonomic ways to go from prototypical intherited objects to pure data records suitable for sending over the wire.
|
||||
### Variables and Constants
|
||||
|
||||
length(value)
|
||||
Length. Find the length of an array in elements, a blob in bits, or a text in codepoints. For functions, it is the arity (or number of inputs).
|
||||
```javascript
|
||||
var x = 10 // mutable variable (block-scoped like let)
|
||||
def PI = 3.14159 // constant (cannot be reassigned)
|
||||
```
|
||||
|
||||
If the value is a record containing a length field
|
||||
### Data Types
|
||||
|
||||
If the length field contains a function, then length(my_record) has the same effect as my_record.length().
|
||||
Cell has six fundamental types:
|
||||
|
||||
If the length field contains a number, return the number.
|
||||
- **number** — DEC64 decimal floating point (no rounding errors)
|
||||
- **text** — Unicode strings
|
||||
- **logical** — `true` or `false`
|
||||
- **null** — the absence of a value (no `undefined`)
|
||||
- **array** — ordered, numerically-indexed sequences
|
||||
- **object** — key-value records with prototype inheritance
|
||||
- **blob** — binary data (bits, not bytes)
|
||||
- **function** — first-class callable values
|
||||
|
||||
All other values produce null.
|
||||
### Literals
|
||||
|
||||
## statements
|
||||
throw
|
||||
Throw an error. If an actor has an uncaught error, it crashes.
|
||||
```javascript
|
||||
// Numbers
|
||||
42
|
||||
3.14
|
||||
1_000_000 // underscores for readability
|
||||
|
||||
## Native constants and functions
|
||||
// Text
|
||||
"hello"
|
||||
'world'
|
||||
`template ${x}` // string interpolation
|
||||
|
||||
false
|
||||
// Logical
|
||||
true
|
||||
false
|
||||
|
||||
// Null
|
||||
null
|
||||
|
||||
use(text)
|
||||
Use a module. Returns the module. Modules are a script which return a single value. The value is cached, and the module is only reloaded when the file changes. The value is stone, so it cannot be modified.
|
||||
|
||||
The returned value is actually a cell runtime owned object, which may be modified for hot reloading purposes.
|
||||
|
||||
stone(value)
|
||||
Petrify the value, turning it into stone. Its contents are preserved, but it can no longer be modified by the assign statement or the blob.write functions. This is usually performed on arrays, records, and blobs. All other types are already stone. Attempting to turn a value that is stone to stone will have no effect.
|
||||
|
||||
The stone operation is deep. Any mutable objects in the value will also be turned to stone.
|
||||
|
||||
This can not be reversed. Immutable objects can never become mutable. A mutable copy can be made of an immutable object, but the original object remains immutable.
|
||||
|
||||
The stone function returns the value.
|
||||
|
||||
isa(object, master_object)
|
||||
Return true if master is in the value's prototype chain.
|
||||
|
||||
isa(object, function)
|
||||
isa(value, function)
|
||||
Return true if function.proto is in the value's prototype chain.
|
||||
|
||||
isa(1, stone) true
|
||||
isa(1, number) true
|
||||
isa("1", stone) true
|
||||
isa("1", text) true
|
||||
isa("1", number) false
|
||||
isa($me, actor) true
|
||||
isa($me, object) false
|
||||
isa($me, stone) true
|
||||
|
||||
isa(object, array)
|
||||
Return true if the object has keys for each value in the array.
|
||||
|
||||
reverse(array|blob)
|
||||
The reverse function makes a new array or blob with the elements or bits in the opposite order.
|
||||
|
||||
It returns a new, reversed array or blob.
|
||||
|
||||
fn
|
||||
The function object; not a function.
|
||||
|
||||
fn.apply(function, array)
|
||||
Apply. Execute the function and return its return value. Pass the elements of the array as input values. See proxy.
|
||||
|
||||
If the first input value is not a function, apply returns its first input value.
|
||||
|
||||
If length(array) is greater than length(function), it disrupts.
|
||||
|
||||
If the second argument is not an array, it is used as a single input value.
|
||||
|
||||
### log
|
||||
log(*name*, *options*)
|
||||
Establish a logging channel.
|
||||
|
||||
log.*channel*(*text)
|
||||
Write to a channel. There are a few already defined:
|
||||
|
||||
log.console
|
||||
log.error
|
||||
|
||||
### text
|
||||
text(*array*, *separator*)
|
||||
Convert an array to text. All are concatenated to create a single text, separated by the separator. The default separator is a space.
|
||||
|
||||
text(*number*, *radix*)
|
||||
Convert a number to text. The radix is 2 through 37.The default radix is 10.
|
||||
|
||||
text(*text*)
|
||||
Return the text.
|
||||
|
||||
text(*text*, *from*, *to*)
|
||||
Return the text from the from index to the to index.
|
||||
|
||||
text.lower(text)
|
||||
The lower function returns a text in which all uppercase characters are converted to lowercase.
|
||||
|
||||
text.normalize(text)
|
||||
Unicode normalize. Return a text whose textual value is the same as text, but whose binary representation is in the specified Unicode normalization form. The two texts will display the same, but might not be equal.
|
||||
|
||||
text.extract(text, pattern, from, to)
|
||||
The text is matched to the pattern. If it does not match, the result is null. If the pattern does match, then the result is a record containing the saved fields.
|
||||
|
||||
text.replace(text, target, replacement, limit)
|
||||
Return a new text in which the target is replaced by the replacement.
|
||||
|
||||
text: the source text.
|
||||
|
||||
target: a text or pattern that should be replaced in the source.
|
||||
|
||||
replacement: text to replace the matched text, or a function that takes the matched text and the starting position, and returns the replacement text or null if it should be left alone.
|
||||
|
||||
limit: The maximum number of replacements. The default is all possible replacements. The limit includes null matches.
|
||||
|
||||
text.format(text, collection, transformer)
|
||||
The format function makes a new text with substitutions in an original text. A collection is either an array of texts or a record of texts.
|
||||
|
||||
A search is made for {left brace and }right brace in the text. If they are found, the middle text between them is examined. If the collection is an array, the middle text is used as a number, and then the matched {left brace and middle and }right brace are replaced with the text at that subscript in the array. If the collection is a record, and if the middle text is the key of a member of the collection with a text value, then the value of the member is used in the substitution. Unmatched text is not altered.
|
||||
|
||||
The text between {left brace and }right brace is broken on the :colon character. The left text will be used as a number or name to select a value from the collection. (The value need not be a text.)
|
||||
|
||||
If a transformer input is a function, then it is called with the collection[left text] and right text as inputs. If it returns a text, then the substitution is made.
|
||||
|
||||
If a transformer input is a record, then the right text is used to select a function from the transformer. That function is passed the value from the collection. If the return value is a text, that text will substitute. If there is no colon, then the empty text is used to select the function from the transformer. If the transformer does not produce a function, or if the function does not return a text, then no replacement occurs. If transformer[right text](collection[left text]) produces a text, then the substitution is made.
|
||||
|
||||
If the substitution is not made, and if collection[left text] is a number, then the right text is used as a format input in calling collection[left text].text(right text). If it returns a text, then the substitution is made.
|
||||
|
||||
text.codepoint(text)
|
||||
The codepoint function returns the codepoint number of the first character of the text. If the input value is not a text, or if it is the empty text, then it returns null.
|
||||
|
||||
text.search(text, target, from)
|
||||
Search the text for the target. If the target is found, return the character position of the left-most part of the match. If the search fails, return null.
|
||||
|
||||
text: the source text.
|
||||
|
||||
target: a text or pattern that should be found in the source.
|
||||
|
||||
from: The starting position for the search. The default is 0, the beginning of the text. If from is negative, it is added to length(text).
|
||||
|
||||
text.trim(text, reject)
|
||||
The trim function removes selected characters from the ends of a text. The default is to remove control characters and spaces.
|
||||
|
||||
text.upper(text)
|
||||
The upper function returns a text in which all lowercase characters are converted to uppercase.
|
||||
|
||||
### logical
|
||||
logical(value)
|
||||
Convert a value to a logical (boolean).
|
||||
If value is 0, false, "false", or null, return false.
|
||||
If value is 1, true, or "true", return true.
|
||||
Otherwise, return null.
|
||||
|
||||
### number
|
||||
number(logical)
|
||||
Result is 1 or 0.
|
||||
|
||||
number(number)
|
||||
Return the number.
|
||||
|
||||
number(text, radix)
|
||||
Convert a text to a number. The radix is 2 through 37. The default radix is 10.
|
||||
|
||||
number(text, format)
|
||||
The number function converts a text into a number.
|
||||
|
||||
If it is unable to (possibly because of a formatting error), it returns null. The format character determines how the text is interpreted. If the format is not one of those listed, then null is returned.
|
||||
|
||||
format radix separator decimal point
|
||||
"" 10 .period
|
||||
"n"
|
||||
"u" _underbar
|
||||
"d" ,comma
|
||||
"s" space
|
||||
"v" .period ,comma
|
||||
"l" dependent on locale
|
||||
"i" _underbar
|
||||
"b" 2
|
||||
"o" 8
|
||||
"h" 16
|
||||
"t" 32
|
||||
"j" 0x- base 16
|
||||
0o- base 8
|
||||
0b- base 2
|
||||
otherwise base 10
|
||||
|
||||
number.whole(number)
|
||||
Return the whole part of a number.
|
||||
4.9 => 4
|
||||
4.2 => 4
|
||||
|
||||
number.fraction(number)
|
||||
Return the fractional part of a number
|
||||
|
||||
number.floor(number, place)
|
||||
If place is 0 or null, the number is rounded down to the greatest integer that is less than or equal to the number. If place is a small positive integer, then the number is rounded down to that many decimal places. For positive numbers, this is like discarding decimal places.
|
||||
|
||||
number.ceiling(number, place)
|
||||
If place is 0 or null, the number is rounded up to the smallest integer that is greater than or equal to the number. If place is a small positive integer, then the number is rounded up to that decimal place.
|
||||
|
||||
number.abs(number)
|
||||
Absolute value. Return the positive form of the number. If the input value is not a number, the result is null.
|
||||
|
||||
number.round(number, place)
|
||||
If place is 0 or null, the number is rounded to the nearest integer. If place is a small integer, then the number is rounded to that many decimal places.
|
||||
|
||||
number.sign(number)
|
||||
The sign function returns -1 if the number is negative, 0 if the number is exactly 0, 1 if the number is positive, and null if it is not a number.
|
||||
|
||||
number.trunc(number, place)
|
||||
The number is truncated toward zero. If the number is positive, the result is the same as floor(place). If the number is negative, the result is the same as ceiling(place).
|
||||
|
||||
If place is a small integer, then the number is rounded down to that many decimal places. This is like discarding decimal places.
|
||||
|
||||
number.min(...vals)
|
||||
Returns the smallest of all vals.
|
||||
|
||||
number.max(...vals)
|
||||
Returns the largest of all vals.
|
||||
|
||||
number.remainder(dividend, divisor)
|
||||
Remainder. For fit integers, the remainder is dividend - ((dividend // divisor) * divisor).
|
||||
|
||||
### array
|
||||
array(number)
|
||||
Create an array of the specified size. All elements are null.
|
||||
|
||||
array(number, initial_value)
|
||||
Make an array. All of the elements are initialized to initial_value.
|
||||
|
||||
number is a non-negative integer, the intended length of the new array.
|
||||
|
||||
If initial_value is a function, then the function is called for each element to produce initialization values. If the function has an arity of 1 or more, it is passed the element number.
|
||||
|
||||
array(array)
|
||||
Copy.
|
||||
|
||||
array(array, function, reverse, exit)
|
||||
Map. Call the function with each element of the array, collecting the return values in a new array. The function is passed each element and its element number.
|
||||
|
||||
function (element, element_nr)
|
||||
If reverse is true, then it starts with the last element and works backwards.
|
||||
|
||||
If exit is not null, then when the function returns the exit value, then the array function returns early. The exit value will not be stored into the new array. If the array was processed normally, then the returned array will be shorter than the input array. If the array was processed in reverse, then the returned array will have the same length as the input array, and the first elements will be null. The elements in the new array that were not set due to an early exit will be set to null.
|
||||
|
||||
array(array, another_array)
|
||||
Concat. Produce a new array that concatenates the array and another_array.
|
||||
|
||||
array(array, from, to)
|
||||
Slice. Make a mutable copy of all or part of an array.
|
||||
|
||||
array: the array to copy
|
||||
from: the position at which to start copying. Default: 0, the beginning. If negative, add length(array).
|
||||
|
||||
to: the position at which to stop copying. Default: length(array), the end. If negative, add length(array).
|
||||
|
||||
If, after adjustment, from and to are not valid integers in the proper range, then it returns null. from must be positive and less than or equal to to. to must be less than or equal to length(array).
|
||||
|
||||
array(object)
|
||||
Keys. Make an array containing all of the text keys in the object. The keys are not guaranteed to be in any particular order.
|
||||
|
||||
array(text)
|
||||
Split the text into grapheme clusters. A grapheme cluster is a Unicode codepoint and its contributing combining characters, if any.
|
||||
|
||||
array(text, separator)
|
||||
Split the text into an array of subtexts. The separator can be a text or pattern.
|
||||
|
||||
array(text, length)
|
||||
Dice the text into an array of subtexts of a given length.
|
||||
|
||||
array.reduce(array, function, initial, reverse)
|
||||
Reduce. The reduce function takes a function that takes two input values and returns a value.
|
||||
|
||||
function (first, second) {
|
||||
return ...
|
||||
// Arrays
|
||||
[1, 2, 3]
|
||||
["a", "b", "c"]
|
||||
|
||||
// Objects
|
||||
{name: "cell", version: 1}
|
||||
{x: 10, y: 20}
|
||||
```
|
||||
|
||||
### Operators
|
||||
|
||||
```javascript
|
||||
// Arithmetic
|
||||
+ - * / %
|
||||
** // exponentiation
|
||||
|
||||
// Comparison (always strict)
|
||||
== // equals (like === in JS)
|
||||
!= // not equals (like !== in JS)
|
||||
< > <= >=
|
||||
|
||||
// Logical
|
||||
&& || !
|
||||
|
||||
// Assignment
|
||||
= += -= *= /=
|
||||
```
|
||||
|
||||
### Control Flow
|
||||
|
||||
```javascript
|
||||
// Conditionals
|
||||
if (x > 0) {
|
||||
log.console("positive")
|
||||
} else if (x < 0) {
|
||||
log.console("negative")
|
||||
} else {
|
||||
log.console("zero")
|
||||
}
|
||||
The function is called for each element of the array, passing the result of the previous iteration to the next iteration.
|
||||
|
||||
The initial value is optional. If present, the function is called for every element of the array.
|
||||
// Ternary
|
||||
var sign = x > 0 ? 1 : -1
|
||||
|
||||
If initial is null:
|
||||
// Loops
|
||||
for (var i = 0; i < 10; i++) {
|
||||
log.console(i)
|
||||
}
|
||||
|
||||
If length(array) is 0, then it returns null.
|
||||
If length(array) is 1, then it returns array[0].
|
||||
If length(array) is 2, it returns function(array[0], array[1]).
|
||||
If length(array) is 3, it returns function(function(array[0], array[1]), array[2]).
|
||||
And so on.
|
||||
If initial is not null:
|
||||
for (var item of items) {
|
||||
log.console(item)
|
||||
}
|
||||
|
||||
If length(array) is 0, then it returns initial.
|
||||
If length(array) is 1, then it returns function(initial, array[0]).
|
||||
If length(array) is 2, it returns function(function(initial, array[0]), array[1]).
|
||||
If length(array) is 3, it returns function(function(function(initial, array[0]), array[1]), array[2]).
|
||||
And so on.
|
||||
If reverse is true, then the work begins at the end of the array and works backward.
|
||||
for (var key in obj) {
|
||||
log.console(key, obj[key])
|
||||
}
|
||||
|
||||
array.for(array, function, reverse, exit)
|
||||
For each. Call the function with each element of the array. The function is passed each element and its element number.
|
||||
while (condition) {
|
||||
// body
|
||||
}
|
||||
|
||||
(element, element_nr)
|
||||
If reverse is true, then it starts with the last element and works backwards.
|
||||
// Control
|
||||
break
|
||||
continue
|
||||
return value
|
||||
throw "error message"
|
||||
```
|
||||
|
||||
If exit is not null, then when the function returns the exit value, then the for function returns early. The exit value is usually true or false, but it may be anything. If exit is null, then every element is processed.
|
||||
### Functions
|
||||
|
||||
The for function returns null unless it did an early exit, when it returns the exit value.
|
||||
```javascript
|
||||
// Named function
|
||||
function add(a, b) {
|
||||
return a + b
|
||||
}
|
||||
|
||||
array.find(array, function, reverse, from)
|
||||
Call the function for each element of the array, passing each element and its element number.
|
||||
// Anonymous function
|
||||
var multiply = function(a, b) {
|
||||
return a * b
|
||||
}
|
||||
|
||||
(element, element_nr)
|
||||
If the function returns true, then find returns the element number of the current element.
|
||||
// Arrow function
|
||||
var square = x => x * x
|
||||
var sum = (a, b) => a + b
|
||||
|
||||
If the second input value is not a function, then it is compared exactly to the elements.
|
||||
// Rest parameters
|
||||
function log_all(...args) {
|
||||
for (var arg of args) log.console(arg)
|
||||
}
|
||||
|
||||
If the reverse input value is true, then search begins at the end of the array and works backward.
|
||||
// Default parameters
|
||||
function greet(name, greeting = "Hello") {
|
||||
return `${greeting}, ${name}!`
|
||||
}
|
||||
```
|
||||
|
||||
The from input value gives the element number to search first. The default is 0 unless reverse is true, when the default is length(array) - 1.
|
||||
All closures capture `this` (like arrow functions in JavaScript).
|
||||
|
||||
find returns the element number of the found value. If nothing is found, find returns null.
|
||||
## Arrays
|
||||
|
||||
array.filter(array, function)
|
||||
The filter function calls a function for every element in the array, passing each element and its element number.
|
||||
Arrays are **distinct from objects**. They are ordered, numerically-indexed sequences. You cannot add arbitrary string keys to an array.
|
||||
|
||||
(element, element_nr)
|
||||
When the function's return value is true, then the element is copied into a new array. If the function's return value is false, then the element is not copied into the new array. If the return value is not a logical, then the filter returns null.
|
||||
```javascript
|
||||
var arr = [1, 2, 3]
|
||||
arr[0] // 1
|
||||
arr[2] = 10 // [1, 2, 10]
|
||||
length(arr) // 3
|
||||
|
||||
It returns a new array. The length of the new array is between 0 thru length(array). It returns null if the function input is not a function.
|
||||
// Array spread
|
||||
var more = [...arr, 4, 5] // [1, 2, 10, 4, 5]
|
||||
```
|
||||
|
||||
array.sort(array, select)
|
||||
The sort function produces a new array in which the values are sorted. Sort keys must be either all numbers or all texts. Any other type of key or any error in the key calculation will cause the sort to fail, returning null. The sort is ascending. The sort is stable, so the relative order of equal keys is preserved.
|
||||
## Objects
|
||||
|
||||
The optional select input determines how the sort key for each element is selected.
|
||||
Objects are key-value records with prototype-based inheritance.
|
||||
|
||||
select type Sort key for array[index] Description
|
||||
null array[index] The sort key is the element itself. This is useful for sorting simple arrays of numbers or texts.
|
||||
text array[index][select] The sort key is the select field of each record element. This is useful for sorting arrays of records.
|
||||
number array[index][select] The sort key is the select element of each array element. This is useful for sorting arrays of arrays.
|
||||
array select[index] select is an array of the same length containing the sort keys.
|
||||
It returns a new, sorted array.
|
||||
```javascript
|
||||
var point = {x: 10, y: 20}
|
||||
point.x // 10
|
||||
point["y"] // 20
|
||||
|
||||
### object
|
||||
object(object)
|
||||
Shallow mutable copy. Text keys
|
||||
// Object spread
|
||||
var point3d = {...point, z: 30}
|
||||
|
||||
splat(object)
|
||||
Create a new object with the same fields as the object, compressed down its prototype chain, for all keys.
|
||||
// Prototype inheritance
|
||||
var colored_point = {__proto__: point, color: "red"}
|
||||
colored_point.x // 10 (inherited)
|
||||
```
|
||||
|
||||
The only types in a splat will be objects, arrays, numbers, boolean, and text.
|
||||
### Prototypes
|
||||
|
||||
When sending an object with $send, it is automatically splat'd.
|
||||
```javascript
|
||||
// Create object with prototype
|
||||
var child = meme(parent)
|
||||
|
||||
meme(record, record)
|
||||
create a new record with the first as its prototype, and add the second's fields. prototypes cannot be changed.
|
||||
// Get prototype
|
||||
var p = proto(child)
|
||||
|
||||
proto(object)
|
||||
Return the object's prototype.
|
||||
// Check prototype chain
|
||||
isa(child, parent) // true
|
||||
```
|
||||
|
||||
object(object, another_object)
|
||||
Combine. Make a copy of a object, and then put all the fields of another_object into the copy.
|
||||
## Immutability with Stone
|
||||
|
||||
object(object, array_of_keys)
|
||||
Select. Make a new object containing only the fields that are named by the array_of_keys.
|
||||
The `stone()` function makes values permanently immutable.
|
||||
|
||||
object(array_of_keys)
|
||||
Set. Make a object using the array as the source of the keys. Each field value is true.
|
||||
```javascript
|
||||
var config = stone({
|
||||
debug: true,
|
||||
maxRetries: 3
|
||||
})
|
||||
|
||||
object(array_of_keys, value)
|
||||
Value Set. Make a object using the array as the source of the keys. Each field value is value.
|
||||
config.debug = false // Error! Stone objects cannot be modified
|
||||
```
|
||||
|
||||
object(array_of_keys, function)
|
||||
Functional Value Set. Make a object using the array as the source of the keys. The function is called for each key, yielding the field values.
|
||||
Stone is **deep** — all nested objects and arrays are also frozen. This cannot be reversed.
|
||||
|
||||
### parallelism
|
||||
Callbacks
|
||||
A callback function is a function that is used to deliver functional results from the future. A callback function has this signature:
|
||||
```javascript
|
||||
stone.p(value) // returns true if value is stone
|
||||
```
|
||||
|
||||
function (value, reason)
|
||||
The value is the value of the operation if it was successful. The value is null if it is unsuccessful.
|
||||
## Built-in Functions
|
||||
|
||||
If the value is null, then the optional reason may include an error message or indication of the cause of the unsuccess.
|
||||
### length(value)
|
||||
|
||||
Requestor functions take a callback.
|
||||
Returns the length of arrays (elements), text (codepoints), blobs (bits), or functions (arity).
|
||||
|
||||
Requestors
|
||||
A requestor function encapsulates a unit of work, communicating the result thru a callback, allowing work to progress over many turns. A requestor function has this signature:
|
||||
```javascript
|
||||
length([1, 2, 3]) // 3
|
||||
length("hello") // 5
|
||||
length(function(a,b){}) // 2
|
||||
```
|
||||
|
||||
function requestor(callback, value)
|
||||
When the requestor function is finished, it calls the callback function with the result.
|
||||
### use(path)
|
||||
|
||||
The optional value is some value that is used to determine the result.
|
||||
Import a module. Returns the cached, stone value.
|
||||
|
||||
A common usage is to send a message to some actor. When the reply eventually arrives, extract a result from the reply and pass it to the callback.
|
||||
```javascript
|
||||
var math = use('math/radians')
|
||||
var json = use('json')
|
||||
```
|
||||
|
||||
A requestor function can optionally return a cancel function.
|
||||
### isa(value, type)
|
||||
|
||||
Cancels
|
||||
A requestor function may optionally return a cancel function. When the cancel function is called, it attempts to stop the work of the requestor. A cancel function try to send a message to some actor informing it that the result is no longer needed. The purpose of cancel is to stop work that is no longer required. It is advisory. It is not an undo. It is not guaranteed, particularly in the case where cancel is called after some actor has completed its work.
|
||||
Check type or prototype chain.
|
||||
|
||||
Cancel is most effective with the requestor factories that are organizing the work.
|
||||
```javascript
|
||||
isa(42, number) // true
|
||||
isa("hi", text) // true
|
||||
isa([1,2], array) // true
|
||||
isa({}, object) // true
|
||||
isa(child, parent) // true if parent is in prototype chain
|
||||
```
|
||||
|
||||
A cancel function has this signature:
|
||||
### reverse(array)
|
||||
|
||||
function cancel(reason, abandon | true)
|
||||
The reason is optional, but if included might be logged or propagated. If abandon is true, then simply stop the requested work. If abandon is false, then if possible, complete the assignment, perhaps by calling the callback with the reason.
|
||||
Returns a new array with elements in reverse order.
|
||||
|
||||
parallel(requestor_array, throttle, need)
|
||||
Parallel. The parallel requestor factory returns a resquestor function. When the requestor function is called with a callback function and a value, every requestor in the requestor_array is called with the value, and the results are placed in corresponding elements of the results array. This all happens in parallel. The value produced by the first element of the requestor_array provides the first element of the result. The completed results array is passed to the callback function.
|
||||
```javascript
|
||||
reverse([1, 2, 3]) // [3, 2, 1]
|
||||
```
|
||||
|
||||
By default, it starts all of the requestors in the requestor_array at once, each in its own turn so that they do not interfere with each other. This can shock some systems by unleashing a lot of demand all at once. To mitigate the shock, the optional throttle argument sets the maximum number of requestors running at a time. As requestors succeed or fail, waiting requestors can be started. The throttle is optional. If provided, the throttle is a number: the maximum number of requestors to have running at any time.
|
||||
### logical(value)
|
||||
|
||||
Ordinarily, the number of successes must be the same as the number of requestors in the requestor_array. If you need few successes, specify your need with the need argument. The need could be 1, meaning that 1 or more successes are needed. The need could be 0, meaning that no successes are needed. If the number of successes is greater than or equal to need, then the whole operation succeeds. The need must be between 0 and requestor_array.length.
|
||||
Convert to boolean.
|
||||
|
||||
The requestor function itself returns a cancel function that cancels all of the pending requestors from the requestor_array.
|
||||
```javascript
|
||||
logical(0) // false
|
||||
logical(1) // true
|
||||
logical("true") // true
|
||||
logical("false") // false
|
||||
logical(null) // false
|
||||
```
|
||||
|
||||
race(requestor_array, throttle, need)
|
||||
Race. The race function is a requestor factory that takes an array of requestor functions and returns a requestor function that starts all of the requestors in the requestor_array at once.
|
||||
## Logging
|
||||
|
||||
By default, it starts all of the requestors in the requestor_array at once, each in its own turn so that they do not interfere with each other. This can shock some systems by unleashing a lot of demand at once. To mitigate the shock, the optional throttle argument sets the maximum number of requestors running at a time. As requestors succeed or fail, waiting requestors can be started.
|
||||
```javascript
|
||||
log.console("message") // standard output
|
||||
log.error("problem") // error output
|
||||
```
|
||||
|
||||
By default, a single result is produced. If an array of results is need, specify the needed number of results in the need parameter. When the needed number of successful results is obtained, the operation ends. The results go into a sparce array aligned with the requestor_array, and unfinished requestors are cancelled. The need argument must be between 1 and requestor_array.length.
|
||||
## Pattern Matching
|
||||
|
||||
The returned requestor function returns a cancel function that cancels all of the pending requestors from the requestor_array.
|
||||
Cell supports regex patterns in string functions, but not standalone regex objects.
|
||||
|
||||
sequence(requestor_array)
|
||||
Sequence. The sequence requestor factory that takes a requestor_array and returns a requestor function that starts all of the requestors in the requestor_array one at a time. The result of each becomes the input to the next. The last result is the result of the sequence.
|
||||
```javascript
|
||||
text.search("hello world", /world/)
|
||||
text.replace("hello", /l/g, "L")
|
||||
```
|
||||
|
||||
sequence returns a requestor that returns a cancel function and processes each requestor in requestor_array one at a time. Each of those requestors is passed the result of the previous requestor as its value argument. If all succeed, then the sequence succeeds, giving the result of the last of the requestors. If any fail, then the sequence fails.The sequence succeeds if every requestor in the requestor_array succeeds
|
||||
## Error Handling
|
||||
|
||||
fallback(requestor_array)
|
||||
The fallback requestor factory returns a requestor function that tries each of the requestors in the requstor_array until it gets a success. When the requestor is called, it calls the first requestor in requestor_array. If that is eventually successful, its value is passed to the callback. But if that requestor fails, the next requestor is called, and so on. If none of the requestors is successful, then the fallback fails. If any one succeeds, then the fallback succeeds.
|
||||
```javascript
|
||||
try {
|
||||
riskyOperation()
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
}
|
||||
|
||||
The fallback requestor returns a cancel function that can be called when the result is no longer needed.
|
||||
throw "something went wrong"
|
||||
```
|
||||
|
||||
## Standard library
|
||||
|
||||
### time
|
||||
use('time')
|
||||
|
||||
constants
|
||||
time.second : 1
|
||||
The number of seconds in a second.
|
||||
|
||||
time.minute : 60
|
||||
The number of seconds in a minute.
|
||||
|
||||
time.hour : 3_600
|
||||
The number of seconds in an hour of 60 minutes.
|
||||
|
||||
time.day : 86_400
|
||||
The number of seconds in a day of 24 hours.
|
||||
|
||||
time.week : 604_800
|
||||
The number of seconds in a week of 7 days.
|
||||
|
||||
time.month : 2_629_746
|
||||
The number of seconds in a Gregorian month of 30.436875 days.
|
||||
|
||||
time.year : 31_556_952
|
||||
The number of seconds in a Gregorian year of 365.2425 days
|
||||
|
||||
time has three functions: time.number(), time.record(), time.text().
|
||||
|
||||
time.number() returns the time now as a number of seconds since the epoch.
|
||||
time.record() returns the time now as a record, with year, month, day, hour, minute, second, and nanosecond.
|
||||
time.text(format) returns the time now as a text, using the format string. The standard format string is "yyyy-MM-dd HH:mm:ss.SSS".
|
||||
|
||||
Pass a time into a function to convert it.
|
||||
|
||||
It's easy to say "tomorrow": time.number() + time.day = this time tomorrow. As a string: time.text(time.number() + time.day).
|
||||
|
||||
time.number(text, format, zone)
|
||||
time.number(record)
|
||||
returns the time as a number of seconds since the epoch.
|
||||
|
||||
time.text(number, format, zone)
|
||||
time.text(record, format, zone)
|
||||
returns the time as a text, using the format string. The standard format string is "yyyy-MM-dd HH:mm:ss.SSS".
|
||||
|
||||
time.record(number)
|
||||
time.record(text, format, zone)
|
||||
|
||||
### math
|
||||
There are three math variants: use('math/radians'), use('math/degrees'), use('math/cycles'). Each has the same functions, but with different representation of angles.
|
||||
|
||||
arc_cosine(number)
|
||||
arc_sine(number)
|
||||
arc_tangent(number, denominator)
|
||||
Compute the arc tangent of a If the optional denominator is supplied, then the value is obtained from number / denominator, using the signs of both to determine the quadrant. The denominator is allowed to be 0. The result is the inverse tangent, in radians, between neg(π) and π.
|
||||
|
||||
e(power = 1)
|
||||
ln(number)
|
||||
Natural log
|
||||
|
||||
log(number)
|
||||
Base 10 log
|
||||
|
||||
log2(number)
|
||||
Base 2 log
|
||||
|
||||
power(first, second)
|
||||
root(radicand, number)
|
||||
sine(number)
|
||||
sqrt(number)
|
||||
tangent(number)
|
||||
|
||||
### blob
|
||||
use('blob')
|
||||
|
||||
A blob is a binary large object, a linear container of bits. Blobs can be used to encode data, messages, sounds, images, public keys, network addresses, and encrypted payloads.
|
||||
|
||||
A blob can be in one of two states, either antestone or stone. In the mutable antestone state, the write functions may be used to append bits to the blob. In the immutable stone state, bits can be harvested from the blob. Bits can be written to blobs as fixed size bit fields, that is a sequence of bits with a specified length, or as a kim.
|
||||
|
||||
blob.make()
|
||||
Make a new empty blob.
|
||||
|
||||
blob.make(capacity)
|
||||
Make a new empty blob with an initial capacity in bits. When turned to stone, the excess bits are discarded. If the initial capacity is too small, the write functions will extended it. A good initial guess can improve performance.
|
||||
|
||||
blob.make(length, logical)
|
||||
Make a new blob containing all zeros (false) or all ones (true).
|
||||
|
||||
blob.make(length, random)
|
||||
Make a new blob of a given length whose content is random. The random input is a random generator function that returns fit numbers, like random.random_fit().
|
||||
|
||||
blob.make(blob, from, to)
|
||||
Make a copy of all or part of a blob. The default of from is 0. The default of to is the length(blob).
|
||||
|
||||
blob.write_bit(blob, logical)
|
||||
Append a bit to the end of the blob. The logical value can be true, false, 1, or 0. Any other value will throw.
|
||||
|
||||
blob.write_blob(blob, second_blob)
|
||||
Append second_blob to the end of blob.
|
||||
|
||||
blob.write_dec64(blob, number)
|
||||
Append a 64 bit DEC64 encoded number to a stone blob.
|
||||
|
||||
blob.write_fit(blob, fit, length)
|
||||
Append a bit field to the blob. If the fit requires more bits than allowed by length, it throws.
|
||||
|
||||
blob.write_kim(blob, fit)
|
||||
Append a fit number or a single character as a kim value.
|
||||
|
||||
blob.write_pad(blob, block_size)
|
||||
Append a 1 bit to the blob followed by enough 0 bits to round up the blob's length to a multiple of the block_size.
|
||||
|
||||
blob.write_text(blob, text)
|
||||
Append a text. This will be encoded as a kim encoded length followed by a sequence of kim encoded UTF-32 characters.
|
||||
|
||||
blob.read_blob(blob, from, to)
|
||||
Make a copy of all or part of a blob. The default of from is 0. The default of to is the length(blob).
|
||||
|
||||
blob.read_dec64(blob, from)
|
||||
Retrieve a 64 bit DEC64 encoded number from a stone blob.
|
||||
|
||||
blob.read_fit(blob, from, length)
|
||||
Retrieve a fit number from a bit field from a stone blob.
|
||||
|
||||
blob.read_kim(blob, from)
|
||||
Retrieve a kim encoded fit number from a stone blob.
|
||||
|
||||
blob.read_logical(blob, from)
|
||||
Retrieve a bit from the blob. If blob is not a stone blob, or if from is out of range, it returns null.
|
||||
|
||||
blob.read_text(blob, from)
|
||||
Retrieve a kim encoded text from a stone blob.
|
||||
|
||||
blob.pad?(blob, from, block_size)
|
||||
Return true if the stone blob's length is a multiple of the block_size (in bits), and if the difference between length and from is less than or equal to the block_size, and if the bit at from is 1, and that any remaining bits are 0. See write_pad.
|
||||
|
||||
### json
|
||||
use('json')
|
||||
|
||||
json.encode(value, space, replacer, whitelist)
|
||||
json.decode(text, reviver)
|
||||
|
||||
### random
|
||||
random.random()
|
||||
The random function returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5.
|
||||
|
||||
random.random_fit()
|
||||
The random_fit function returns an integer in the range -36028797018963968 thru 36028797018963967 that contains 56 random bits. See fit.
|
||||
|
||||
random.random_whole(number)
|
||||
The random_whole function returns a whole number that is greater than or equal to zero and less than the number, which must be less than 36028797018963968.
|
||||
|
||||
## Actors
|
||||
A program has may have access to functions which are not standard which have been bestowed on it. These begin with a '$'.
|
||||
|
||||
$send(actor, message, callback)
|
||||
$clock(callback)
|
||||
$delay(callback, seconds)
|
||||
$time_limit(requestor, seconds)
|
||||
$contact(callback, record)
|
||||
$couple(actor)
|
||||
$portal(callback, port)
|
||||
$receiver(callback)
|
||||
$start(callback, program)
|
||||
$stop(actor)
|
||||
$unneeded(function, seconds)
|
||||
|
||||
$me
|
||||
The actor object of the running program.
|
||||
If an actor has an uncaught error, it crashes.
|
||||
|
||||
171
docs/cli.md
Normal file
171
docs/cli.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Command Line Interface
|
||||
|
||||
Cell provides a command-line interface for managing packages, running scripts, and building applications.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
cell <command> [arguments]
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### cell version
|
||||
|
||||
Display the Cell version.
|
||||
|
||||
```bash
|
||||
cell version
|
||||
# 0.1.0
|
||||
```
|
||||
|
||||
### cell install
|
||||
|
||||
Install a package to the shop.
|
||||
|
||||
```bash
|
||||
cell install gitea.pockle.world/john/prosperon
|
||||
cell install /Users/john/local/mypackage # local path
|
||||
```
|
||||
|
||||
### cell update
|
||||
|
||||
Update packages from remote sources.
|
||||
|
||||
```bash
|
||||
cell update # update all packages
|
||||
cell update <package> # update specific package
|
||||
```
|
||||
|
||||
### cell remove
|
||||
|
||||
Remove a package from the shop.
|
||||
|
||||
```bash
|
||||
cell remove gitea.pockle.world/john/oldpackage
|
||||
```
|
||||
|
||||
### cell list
|
||||
|
||||
List installed packages.
|
||||
|
||||
```bash
|
||||
cell list # list all installed packages
|
||||
cell list <package> # list dependencies of a package
|
||||
```
|
||||
|
||||
### cell ls
|
||||
|
||||
List modules and actors in a package.
|
||||
|
||||
```bash
|
||||
cell ls # list files in current project
|
||||
cell ls <package> # list files in specified package
|
||||
```
|
||||
|
||||
### cell build
|
||||
|
||||
Build the current package.
|
||||
|
||||
```bash
|
||||
cell build
|
||||
```
|
||||
|
||||
### cell test
|
||||
|
||||
Run tests.
|
||||
|
||||
```bash
|
||||
cell test # run tests in current package
|
||||
cell test all # run all tests
|
||||
cell test <package> # run tests in specific package
|
||||
```
|
||||
|
||||
### cell link
|
||||
|
||||
Manage local package links for development.
|
||||
|
||||
```bash
|
||||
cell link add <canonical> <local_path> # link a package
|
||||
cell link list # show all links
|
||||
cell link delete <canonical> # remove a link
|
||||
cell link clear # remove all links
|
||||
```
|
||||
|
||||
### cell fetch
|
||||
|
||||
Fetch package sources without extracting.
|
||||
|
||||
```bash
|
||||
cell fetch <package>
|
||||
```
|
||||
|
||||
### cell upgrade
|
||||
|
||||
Upgrade the Cell installation itself.
|
||||
|
||||
```bash
|
||||
cell upgrade
|
||||
```
|
||||
|
||||
### cell clean
|
||||
|
||||
Clean build artifacts.
|
||||
|
||||
```bash
|
||||
cell clean
|
||||
```
|
||||
|
||||
### cell help
|
||||
|
||||
Display help information.
|
||||
|
||||
```bash
|
||||
cell help
|
||||
cell help <command>
|
||||
```
|
||||
|
||||
## Running Scripts
|
||||
|
||||
Any `.ce` file in the Cell core can be run as a command:
|
||||
|
||||
```bash
|
||||
cell version # runs version.ce
|
||||
cell build # runs build.ce
|
||||
cell test # runs test.ce
|
||||
```
|
||||
|
||||
## Package Locators
|
||||
|
||||
Packages are identified by locators:
|
||||
|
||||
- **Remote**: `gitea.pockle.world/user/repo`
|
||||
- **Local**: `/absolute/path/to/package`
|
||||
|
||||
```bash
|
||||
cell install gitea.pockle.world/john/prosperon
|
||||
cell install /Users/john/work/mylib
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Cell stores its data in `~/.cell/`:
|
||||
|
||||
```
|
||||
~/.cell/
|
||||
├── packages/ # installed packages
|
||||
├── lib/ # compiled dynamic libraries
|
||||
├── build/ # build cache
|
||||
├── cache/ # downloaded archives
|
||||
├── lock.toml # installed package versions
|
||||
└── link.toml # local development links
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
Cell reads the `HOME` environment variable to locate the shop directory.
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` — Success
|
||||
- Non-zero — Error (check output for details)
|
||||
@@ -1,26 +1,66 @@
|
||||
# Preface: The Cell Environment
|
||||
# Cell
|
||||
|
||||

|
||||
|
||||
Cell is an actor based scripting language.
|
||||
Cell is an actor-based scripting language for building concurrent applications. It combines a familiar JavaScript-like syntax with the actor model of computation.
|
||||
|
||||
The idea is to author c code and orchestrate it using cell script, in an actor based environment, and deploy it on many platforms.
|
||||
## Key Features
|
||||
|
||||
## Building code
|
||||
There are two ways to run cell:
|
||||
- **Actor Model** — isolated memory, message passing, no shared state
|
||||
- **Immutability** — `stone()` makes values permanently frozen
|
||||
- **Prototype Inheritance** — objects without classes
|
||||
- **C Integration** — seamlessly extend with native code
|
||||
- **Cross-Platform** — deploy to desktop, web, and embedded
|
||||
|
||||
The dev environment is highly dynamic and available only on platforms with dynamic library loading. It lets you spawn actors and test quickly.
|
||||
## Quick Start
|
||||
|
||||
Cake is a static builder for cell, which lets you bundle a package into a static executable for a variety of target platforms.
|
||||
```javascript
|
||||
// hello.ce - A simple actor
|
||||
log.console("Hello, Cell!")
|
||||
$_.stop()
|
||||
```
|
||||
|
||||
## Packages
|
||||
Cell doesn't assume an underlying file system.
|
||||
```bash
|
||||
cell hello
|
||||
```
|
||||
|
||||
Packages are the fundamental cell unit. A package is made up of a variety of cell script files and potentially C files. When bundling, everything in a package is bundled together; when running in dev, everything in a package is loaded dynamically.
|
||||
## Documentation
|
||||
|
||||
Cell code is written with cellscript.
|
||||
- [**Cell Language**](cellscript.md) — syntax, types, and built-in functions
|
||||
- [**Actors and Modules**](actors.md) — the execution model
|
||||
- [**Packages**](packages.md) — code organization and sharing
|
||||
- [**Command Line**](cli.md) — the `cell` tool
|
||||
- [**Writing C Modules**](c-modules.md) — native extensions
|
||||
|
||||
## Modules and programs
|
||||
A module returns a single object. This can be a function, an object of functions, a number, a string, whatever. It is included with the "use" keyword, ie, use('<package>/<module path>').
|
||||
## Standard Library
|
||||
|
||||
A program doesn't return anything. A program is an actor, with its own memory space. Actors do not share memory. Actors can send messages to each other.
|
||||
- [text](library/text.md) — string manipulation
|
||||
- [number](library/number.md) — numeric operations
|
||||
- [array](library/array.md) — array utilities
|
||||
- [object](library/object.md) — object utilities
|
||||
- [blob](library/blob.md) — binary data
|
||||
- [time](library/time.md) — time and dates
|
||||
- [math](library/math.md) — trigonometry and math
|
||||
- [json](library/json.md) — JSON encoding/decoding
|
||||
- [random](library/random.md) — random numbers
|
||||
|
||||
## Architecture
|
||||
|
||||
Cell programs are organized into **packages**. Each package contains:
|
||||
|
||||
- **Modules** (`.cm`) — return a value, cached and frozen
|
||||
- **Actors** (`.ce`) — run independently, communicate via messages
|
||||
- **C files** (`.c`) — compiled to native libraries
|
||||
|
||||
Actors never share memory. They communicate by sending messages, which are automatically serialized. This makes concurrent programming safe and predictable.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Clone and bootstrap
|
||||
git clone https://gitea.pockle.world/john/cell
|
||||
cd cell
|
||||
make bootstrap
|
||||
```
|
||||
|
||||
The Cell shop is stored at `~/.cell/`.
|
||||
10
docs/library/.pages
Normal file
10
docs/library/.pages
Normal file
@@ -0,0 +1,10 @@
|
||||
nav:
|
||||
- text.md
|
||||
- number.md
|
||||
- array.md
|
||||
- object.md
|
||||
- blob.md
|
||||
- time.md
|
||||
- math.md
|
||||
- json.md
|
||||
- random.md
|
||||
151
docs/library/array.md
Normal file
151
docs/library/array.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# array
|
||||
|
||||
The `array` function and its methods handle array creation and manipulation.
|
||||
|
||||
## Creation
|
||||
|
||||
### array(number)
|
||||
|
||||
Create an array of specified size, filled with `null`.
|
||||
|
||||
```javascript
|
||||
array(3) // [null, null, null]
|
||||
```
|
||||
|
||||
### array(number, initial)
|
||||
|
||||
Create an array with initial values.
|
||||
|
||||
```javascript
|
||||
array(3, 0) // [0, 0, 0]
|
||||
array(3, i => i * 2) // [0, 2, 4]
|
||||
```
|
||||
|
||||
### array(array)
|
||||
|
||||
Copy an array.
|
||||
|
||||
```javascript
|
||||
var copy = array(original)
|
||||
```
|
||||
|
||||
### array(array, from, to)
|
||||
|
||||
Slice an array.
|
||||
|
||||
```javascript
|
||||
array([1, 2, 3, 4, 5], 1, 4) // [2, 3, 4]
|
||||
array([1, 2, 3], -2) // [2, 3]
|
||||
```
|
||||
|
||||
### array(array, another)
|
||||
|
||||
Concatenate arrays.
|
||||
|
||||
```javascript
|
||||
array([1, 2], [3, 4]) // [1, 2, 3, 4]
|
||||
```
|
||||
|
||||
### array(object)
|
||||
|
||||
Get keys of an object.
|
||||
|
||||
```javascript
|
||||
array({a: 1, b: 2}) // ["a", "b"]
|
||||
```
|
||||
|
||||
### array(text)
|
||||
|
||||
Split text into grapheme clusters.
|
||||
|
||||
```javascript
|
||||
array("hello") // ["h", "e", "l", "l", "o"]
|
||||
array("👨👩👧") // ["👨👩👧"]
|
||||
```
|
||||
|
||||
### array(text, separator)
|
||||
|
||||
Split text by separator.
|
||||
|
||||
```javascript
|
||||
array("a,b,c", ",") // ["a", "b", "c"]
|
||||
```
|
||||
|
||||
### array(text, length)
|
||||
|
||||
Split text into chunks.
|
||||
|
||||
```javascript
|
||||
array("abcdef", 2) // ["ab", "cd", "ef"]
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### array.for(arr, fn, reverse, exit)
|
||||
|
||||
Iterate over elements.
|
||||
|
||||
```javascript
|
||||
array.for([1, 2, 3], function(el, i) {
|
||||
log.console(i, el)
|
||||
})
|
||||
|
||||
// With early exit
|
||||
array.for([1, 2, 3, 4], function(el) {
|
||||
if (el > 2) return true
|
||||
log.console(el)
|
||||
}, false, true) // prints 1, 2
|
||||
```
|
||||
|
||||
### array.find(arr, fn, reverse, from)
|
||||
|
||||
Find element index.
|
||||
|
||||
```javascript
|
||||
array.find([1, 2, 3], 2) // 1
|
||||
array.find([1, 2, 3], x => x > 1) // 1
|
||||
array.find([1, 2, 3], x => x > 1, true) // 2 (from end)
|
||||
```
|
||||
|
||||
### array.filter(arr, fn)
|
||||
|
||||
Filter elements.
|
||||
|
||||
```javascript
|
||||
array.filter([1, 2, 3, 4], x => x % 2 == 0) // [2, 4]
|
||||
```
|
||||
|
||||
### array.reduce(arr, fn, initial, reverse)
|
||||
|
||||
Reduce to single value.
|
||||
|
||||
```javascript
|
||||
array.reduce([1, 2, 3, 4], (a, b) => a + b) // 10
|
||||
array.reduce([1, 2, 3, 4], (a, b) => a + b, 10) // 20
|
||||
```
|
||||
|
||||
### array.sort(arr, select)
|
||||
|
||||
Sort array (returns new array).
|
||||
|
||||
```javascript
|
||||
array.sort([3, 1, 4, 1, 5]) // [1, 1, 3, 4, 5]
|
||||
|
||||
// Sort by field
|
||||
array.sort([{n: 3}, {n: 1}], "n") // [{n: 1}, {n: 3}]
|
||||
|
||||
// Sort by index
|
||||
array.sort([[3, "c"], [1, "a"]], 0) // [[1, "a"], [3, "c"]]
|
||||
```
|
||||
|
||||
## Map with array()
|
||||
|
||||
The `array(arr, fn)` form maps over elements:
|
||||
|
||||
```javascript
|
||||
array([1, 2, 3], x => x * 2) // [2, 4, 6]
|
||||
|
||||
array([1, 2, 3], function(el, i) {
|
||||
return el + i
|
||||
}) // [1, 3, 5]
|
||||
```
|
||||
182
docs/library/blob.md
Normal file
182
docs/library/blob.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# blob
|
||||
|
||||
Blobs are binary large objects — containers of bits (not bytes). They're used for encoding data, messages, images, network payloads, and more.
|
||||
|
||||
## States
|
||||
|
||||
A blob exists in one of two states:
|
||||
|
||||
- **antestone** (mutable) — write operations allowed
|
||||
- **stone** (immutable) — read operations allowed
|
||||
|
||||
```javascript
|
||||
var blob = use('blob')
|
||||
|
||||
var b = blob.make() // antestone
|
||||
blob.write_bit(b, 1)
|
||||
blob.write_fit(b, 42, 8)
|
||||
stone(b) // now stone, readable
|
||||
```
|
||||
|
||||
## Creation
|
||||
|
||||
### blob.make()
|
||||
|
||||
Create an empty blob.
|
||||
|
||||
```javascript
|
||||
var b = blob.make()
|
||||
```
|
||||
|
||||
### blob.make(capacity)
|
||||
|
||||
Create with initial capacity (bits).
|
||||
|
||||
```javascript
|
||||
var b = blob.make(1024) // 1024 bits capacity
|
||||
```
|
||||
|
||||
### blob.make(length, logical)
|
||||
|
||||
Create filled with zeros or ones.
|
||||
|
||||
```javascript
|
||||
blob.make(64, false) // 64 zero bits
|
||||
blob.make(64, true) // 64 one bits
|
||||
```
|
||||
|
||||
### blob.make(blob, from, to)
|
||||
|
||||
Copy a range from another blob.
|
||||
|
||||
```javascript
|
||||
var slice = blob.make(original, 0, 32) // first 32 bits
|
||||
```
|
||||
|
||||
## Writing (antestone only)
|
||||
|
||||
### blob.write_bit(b, logical)
|
||||
|
||||
Append a single bit.
|
||||
|
||||
```javascript
|
||||
blob.write_bit(b, true) // append 1
|
||||
blob.write_bit(b, false) // append 0
|
||||
```
|
||||
|
||||
### blob.write_fit(b, value, length)
|
||||
|
||||
Append a fixed-width integer.
|
||||
|
||||
```javascript
|
||||
blob.write_fit(b, 255, 8) // 8-bit value
|
||||
blob.write_fit(b, 1000, 16) // 16-bit value
|
||||
```
|
||||
|
||||
### blob.write_blob(b, other)
|
||||
|
||||
Append another blob's contents.
|
||||
|
||||
```javascript
|
||||
blob.write_blob(b, other_blob)
|
||||
```
|
||||
|
||||
### blob.write_dec64(b, number)
|
||||
|
||||
Append a 64-bit DEC64 number.
|
||||
|
||||
```javascript
|
||||
blob.write_dec64(b, 3.14159)
|
||||
```
|
||||
|
||||
### blob.write_text(b, text)
|
||||
|
||||
Append text (kim-encoded length + characters).
|
||||
|
||||
```javascript
|
||||
blob.write_text(b, "hello")
|
||||
```
|
||||
|
||||
### blob.write_pad(b, block_size)
|
||||
|
||||
Pad to block boundary (1 bit + zeros).
|
||||
|
||||
```javascript
|
||||
blob.write_pad(b, 8) // pad to byte boundary
|
||||
```
|
||||
|
||||
## Reading (stone only)
|
||||
|
||||
### blob.read_logical(b, from)
|
||||
|
||||
Read a single bit.
|
||||
|
||||
```javascript
|
||||
var bit = blob.read_logical(b, 0) // first bit
|
||||
```
|
||||
|
||||
### blob.read_fit(b, from, length)
|
||||
|
||||
Read a fixed-width integer.
|
||||
|
||||
```javascript
|
||||
var value = blob.read_fit(b, 0, 8) // read 8 bits from position 0
|
||||
```
|
||||
|
||||
### blob.read_blob(b, from, to)
|
||||
|
||||
Extract a range as new blob.
|
||||
|
||||
```javascript
|
||||
var slice = blob.read_blob(b, 8, 24) // bits 8-23
|
||||
```
|
||||
|
||||
### blob.read_dec64(b, from)
|
||||
|
||||
Read a 64-bit DEC64 number.
|
||||
|
||||
```javascript
|
||||
var num = blob.read_dec64(b, 0)
|
||||
```
|
||||
|
||||
### blob.read_text(b, from)
|
||||
|
||||
Read kim-encoded text.
|
||||
|
||||
```javascript
|
||||
var str = blob.read_text(b, 0)
|
||||
```
|
||||
|
||||
### blob.pad?(b, from, block_size)
|
||||
|
||||
Check if padding is valid.
|
||||
|
||||
```javascript
|
||||
if (blob["pad?"](b, pos, 8)) {
|
||||
// valid byte-aligned padding
|
||||
}
|
||||
```
|
||||
|
||||
## Length
|
||||
|
||||
```javascript
|
||||
length(b) // returns bit count
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
var blob = use('blob')
|
||||
|
||||
// Encode a simple message
|
||||
var msg = blob.make()
|
||||
blob.write_fit(msg, 1, 8) // message type
|
||||
blob.write_fit(msg, 42, 32) // payload
|
||||
blob.write_text(msg, "hello") // text data
|
||||
stone(msg)
|
||||
|
||||
// Decode
|
||||
var type = blob.read_fit(msg, 0, 8)
|
||||
var payload = blob.read_fit(msg, 8, 32)
|
||||
var txt = blob.read_text(msg, 40)
|
||||
```
|
||||
90
docs/library/json.md
Normal file
90
docs/library/json.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# json
|
||||
|
||||
JSON encoding and decoding.
|
||||
|
||||
```javascript
|
||||
var json = use('json')
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### json.encode(value, space, replacer, whitelist)
|
||||
|
||||
Convert a value to JSON text.
|
||||
|
||||
```javascript
|
||||
json.encode({a: 1, b: 2})
|
||||
// '{"a":1,"b":2}'
|
||||
|
||||
// Pretty print with 2-space indent
|
||||
json.encode({a: 1, b: 2}, 2)
|
||||
// '{
|
||||
// "a": 1,
|
||||
// "b": 2
|
||||
// }'
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **value** — the value to encode
|
||||
- **space** — indentation (number of spaces or string)
|
||||
- **replacer** — function to transform values
|
||||
- **whitelist** — array of keys to include
|
||||
|
||||
```javascript
|
||||
// With replacer
|
||||
json.encode({a: 1, b: 2}, null, function(key, value) {
|
||||
if (key == "b") return value * 10
|
||||
return value
|
||||
})
|
||||
// '{"a":1,"b":20}'
|
||||
|
||||
// With whitelist
|
||||
json.encode({a: 1, b: 2, c: 3}, null, null, ["a", "c"])
|
||||
// '{"a":1,"c":3}'
|
||||
```
|
||||
|
||||
## Decoding
|
||||
|
||||
### json.decode(text, reviver)
|
||||
|
||||
Parse JSON text to a value.
|
||||
|
||||
```javascript
|
||||
json.decode('{"a":1,"b":2}')
|
||||
// {a: 1, b: 2}
|
||||
|
||||
json.decode('[1, 2, 3]')
|
||||
// [1, 2, 3]
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **text** — JSON string to parse
|
||||
- **reviver** — function to transform parsed values
|
||||
|
||||
```javascript
|
||||
// With reviver
|
||||
json.decode('{"date":"2024-01-15"}', function(key, value) {
|
||||
if (key == "date") return parse_date(value)
|
||||
return value
|
||||
})
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
var json = use('json')
|
||||
|
||||
// Save configuration
|
||||
var config = {
|
||||
debug: true,
|
||||
maxRetries: 3,
|
||||
endpoints: ["api.example.com"]
|
||||
}
|
||||
var config_text = json.encode(config, 2)
|
||||
|
||||
// Load configuration
|
||||
var loaded = json.decode(config_text)
|
||||
log.console(loaded.debug) // true
|
||||
```
|
||||
153
docs/library/math.md
Normal file
153
docs/library/math.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# math
|
||||
|
||||
Cell provides three math modules with identical functions but different angle representations:
|
||||
|
||||
```javascript
|
||||
var math = use('math/radians') // angles in radians
|
||||
var math = use('math/degrees') // angles in degrees
|
||||
var math = use('math/cycles') // angles in cycles (0-1)
|
||||
```
|
||||
|
||||
## Trigonometry
|
||||
|
||||
### sine(angle)
|
||||
|
||||
```javascript
|
||||
math.sine(math.pi / 2) // 1 (radians)
|
||||
math.sine(90) // 1 (degrees)
|
||||
math.sine(0.25) // 1 (cycles)
|
||||
```
|
||||
|
||||
### cosine(angle)
|
||||
|
||||
```javascript
|
||||
math.cosine(0) // 1
|
||||
```
|
||||
|
||||
### tangent(angle)
|
||||
|
||||
```javascript
|
||||
math.tangent(math.pi / 4) // 1 (radians)
|
||||
```
|
||||
|
||||
### arc_sine(n)
|
||||
|
||||
Inverse sine.
|
||||
|
||||
```javascript
|
||||
math.arc_sine(1) // π/2 (radians)
|
||||
```
|
||||
|
||||
### arc_cosine(n)
|
||||
|
||||
Inverse cosine.
|
||||
|
||||
```javascript
|
||||
math.arc_cosine(0) // π/2 (radians)
|
||||
```
|
||||
|
||||
### arc_tangent(n, denominator)
|
||||
|
||||
Inverse tangent. With two arguments, computes atan2.
|
||||
|
||||
```javascript
|
||||
math.arc_tangent(1) // π/4 (radians)
|
||||
math.arc_tangent(1, 1) // π/4 (radians)
|
||||
math.arc_tangent(-1, -1) // -3π/4 (radians)
|
||||
```
|
||||
|
||||
## Exponentials and Logarithms
|
||||
|
||||
### e(power)
|
||||
|
||||
Euler's number raised to a power. Default power is 1.
|
||||
|
||||
```javascript
|
||||
math.e() // 2.718281828...
|
||||
math.e(2) // e²
|
||||
```
|
||||
|
||||
### ln(n)
|
||||
|
||||
Natural logarithm (base e).
|
||||
|
||||
```javascript
|
||||
math.ln(math.e()) // 1
|
||||
```
|
||||
|
||||
### log(n)
|
||||
|
||||
Base 10 logarithm.
|
||||
|
||||
```javascript
|
||||
math.log(100) // 2
|
||||
```
|
||||
|
||||
### log2(n)
|
||||
|
||||
Base 2 logarithm.
|
||||
|
||||
```javascript
|
||||
math.log2(8) // 3
|
||||
```
|
||||
|
||||
## Powers and Roots
|
||||
|
||||
### power(base, exponent)
|
||||
|
||||
```javascript
|
||||
math.power(2, 10) // 1024
|
||||
```
|
||||
|
||||
### sqrt(n)
|
||||
|
||||
Square root.
|
||||
|
||||
```javascript
|
||||
math.sqrt(16) // 4
|
||||
```
|
||||
|
||||
### root(radicand, n)
|
||||
|
||||
Nth root.
|
||||
|
||||
```javascript
|
||||
math.root(27, 3) // 3 (cube root)
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
Available in the radians module:
|
||||
|
||||
```javascript
|
||||
math.pi // 3.14159...
|
||||
math.e() // 2.71828...
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
var math = use('math/radians')
|
||||
|
||||
// Distance between two points
|
||||
function distance(x1, y1, x2, y2) {
|
||||
var dx = x2 - x1
|
||||
var dy = y2 - y1
|
||||
return math.sqrt(dx * dx + dy * dy)
|
||||
}
|
||||
|
||||
// Angle between two points
|
||||
function angle(x1, y1, x2, y2) {
|
||||
return math.arc_tangent(y2 - y1, x2 - x1)
|
||||
}
|
||||
|
||||
// Rotate a point
|
||||
function rotate(x, y, angle) {
|
||||
var c = math.cosine(angle)
|
||||
var s = math.sine(angle)
|
||||
return {
|
||||
x: x * c - y * s,
|
||||
y: x * s + y * c
|
||||
}
|
||||
}
|
||||
```
|
||||
143
docs/library/number.md
Normal file
143
docs/library/number.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# number
|
||||
|
||||
The `number` function and its methods handle numeric conversion and operations.
|
||||
|
||||
## Conversion
|
||||
|
||||
### number(logical)
|
||||
|
||||
Convert boolean to number.
|
||||
|
||||
```javascript
|
||||
number(true) // 1
|
||||
number(false) // 0
|
||||
```
|
||||
|
||||
### number(text, radix)
|
||||
|
||||
Parse text to number. Radix is 2-36 (default: 10).
|
||||
|
||||
```javascript
|
||||
number("42") // 42
|
||||
number("ff", 16) // 255
|
||||
number("1010", 2) // 10
|
||||
```
|
||||
|
||||
### number(text, format)
|
||||
|
||||
Parse formatted numbers.
|
||||
|
||||
| Format | Description |
|
||||
|--------|-------------|
|
||||
| `""` | Standard decimal |
|
||||
| `"u"` | Underbar separator (1_000) |
|
||||
| `"d"` | Comma separator (1,000) |
|
||||
| `"s"` | Space separator (1 000) |
|
||||
| `"v"` | European (1.000,50) |
|
||||
| `"b"` | Binary |
|
||||
| `"o"` | Octal |
|
||||
| `"h"` | Hexadecimal |
|
||||
| `"j"` | JavaScript style (0x, 0o, 0b prefixes) |
|
||||
|
||||
```javascript
|
||||
number("1,000", "d") // 1000
|
||||
number("0xff", "j") // 255
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### number.abs(n)
|
||||
|
||||
Absolute value.
|
||||
|
||||
```javascript
|
||||
number.abs(-5) // 5
|
||||
number.abs(5) // 5
|
||||
```
|
||||
|
||||
### number.sign(n)
|
||||
|
||||
Returns -1, 0, or 1.
|
||||
|
||||
```javascript
|
||||
number.sign(-5) // -1
|
||||
number.sign(0) // 0
|
||||
number.sign(5) // 1
|
||||
```
|
||||
|
||||
### number.floor(n, place)
|
||||
|
||||
Round down.
|
||||
|
||||
```javascript
|
||||
number.floor(4.9) // 4
|
||||
number.floor(4.567, 2) // 4.56
|
||||
```
|
||||
|
||||
### number.ceiling(n, place)
|
||||
|
||||
Round up.
|
||||
|
||||
```javascript
|
||||
number.ceiling(4.1) // 5
|
||||
number.ceiling(4.123, 2) // 4.13
|
||||
```
|
||||
|
||||
### number.round(n, place)
|
||||
|
||||
Round to nearest.
|
||||
|
||||
```javascript
|
||||
number.round(4.5) // 5
|
||||
number.round(4.567, 2) // 4.57
|
||||
```
|
||||
|
||||
### number.trunc(n, place)
|
||||
|
||||
Truncate toward zero.
|
||||
|
||||
```javascript
|
||||
number.trunc(4.9) // 4
|
||||
number.trunc(-4.9) // -4
|
||||
```
|
||||
|
||||
### number.whole(n)
|
||||
|
||||
Get the integer part.
|
||||
|
||||
```javascript
|
||||
number.whole(4.9) // 4
|
||||
number.whole(-4.9) // -4
|
||||
```
|
||||
|
||||
### number.fraction(n)
|
||||
|
||||
Get the fractional part.
|
||||
|
||||
```javascript
|
||||
number.fraction(4.75) // 0.75
|
||||
```
|
||||
|
||||
### number.min(...values)
|
||||
|
||||
Return the smallest value.
|
||||
|
||||
```javascript
|
||||
number.min(3, 1, 4, 1, 5) // 1
|
||||
```
|
||||
|
||||
### number.max(...values)
|
||||
|
||||
Return the largest value.
|
||||
|
||||
```javascript
|
||||
number.max(3, 1, 4, 1, 5) // 5
|
||||
```
|
||||
|
||||
### number.remainder(dividend, divisor)
|
||||
|
||||
Compute remainder.
|
||||
|
||||
```javascript
|
||||
number.remainder(17, 5) // 2
|
||||
```
|
||||
112
docs/library/object.md
Normal file
112
docs/library/object.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# object
|
||||
|
||||
The `object` function and related utilities handle object creation and manipulation.
|
||||
|
||||
## Creation
|
||||
|
||||
### object(obj)
|
||||
|
||||
Shallow copy an object.
|
||||
|
||||
```javascript
|
||||
var copy = object(original)
|
||||
```
|
||||
|
||||
### object(obj, another)
|
||||
|
||||
Combine two objects.
|
||||
|
||||
```javascript
|
||||
object({a: 1}, {b: 2}) // {a: 1, b: 2}
|
||||
object({a: 1}, {a: 2}) // {a: 2}
|
||||
```
|
||||
|
||||
### object(obj, keys)
|
||||
|
||||
Select specific keys.
|
||||
|
||||
```javascript
|
||||
object({a: 1, b: 2, c: 3}, ["a", "c"]) // {a: 1, c: 3}
|
||||
```
|
||||
|
||||
### object(keys)
|
||||
|
||||
Create object from keys (values are `true`).
|
||||
|
||||
```javascript
|
||||
object(["a", "b", "c"]) // {a: true, b: true, c: true}
|
||||
```
|
||||
|
||||
### object(keys, value)
|
||||
|
||||
Create object from keys with specified value.
|
||||
|
||||
```javascript
|
||||
object(["a", "b"], 0) // {a: 0, b: 0}
|
||||
```
|
||||
|
||||
### object(keys, fn)
|
||||
|
||||
Create object from keys with computed values.
|
||||
|
||||
```javascript
|
||||
object(["a", "b", "c"], (k, i) => i) // {a: 0, b: 1, c: 2}
|
||||
```
|
||||
|
||||
## Prototypes
|
||||
|
||||
### meme(prototype)
|
||||
|
||||
Create a new object with the given prototype.
|
||||
|
||||
```javascript
|
||||
var animal = {speak: function() { log.console("...") }}
|
||||
var dog = meme(animal)
|
||||
dog.speak = function() { log.console("woof") }
|
||||
```
|
||||
|
||||
### proto(obj)
|
||||
|
||||
Get an object's prototype.
|
||||
|
||||
```javascript
|
||||
var p = proto(dog) // animal
|
||||
```
|
||||
|
||||
### isa(obj, prototype)
|
||||
|
||||
Check if prototype is in the chain.
|
||||
|
||||
```javascript
|
||||
isa(dog, animal) // true
|
||||
```
|
||||
|
||||
## Serialization
|
||||
|
||||
### splat(obj)
|
||||
|
||||
Flatten an object's prototype chain into a plain object. Only includes primitive types (numbers, text, booleans, arrays, objects).
|
||||
|
||||
```javascript
|
||||
var base = {x: 1}
|
||||
var derived = meme(base)
|
||||
derived.y = 2
|
||||
|
||||
splat(derived) // {x: 1, y: 2}
|
||||
```
|
||||
|
||||
When sending objects between actors with `$send`, they are automatically splatted.
|
||||
|
||||
## Key Iteration
|
||||
|
||||
```javascript
|
||||
var obj = {a: 1, b: 2, c: 3}
|
||||
|
||||
// Get all keys
|
||||
var keys = array(obj) // ["a", "b", "c"]
|
||||
|
||||
// Iterate
|
||||
for (var key in obj) {
|
||||
log.console(key, obj[key])
|
||||
}
|
||||
```
|
||||
71
docs/library/random.md
Normal file
71
docs/library/random.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# random
|
||||
|
||||
Random number generation.
|
||||
|
||||
```javascript
|
||||
var random = use('random')
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### random.random()
|
||||
|
||||
Returns a number between 0 (inclusive) and 1 (exclusive).
|
||||
|
||||
```javascript
|
||||
random.random() // e.g., 0.7234...
|
||||
```
|
||||
|
||||
### random.random_fit()
|
||||
|
||||
Returns a random 56-bit integer in the range -36028797018963968 to 36028797018963967.
|
||||
|
||||
```javascript
|
||||
random.random_fit() // e.g., 12345678901234
|
||||
```
|
||||
|
||||
### random.random_whole(max)
|
||||
|
||||
Returns a whole number from 0 (inclusive) to max (exclusive).
|
||||
|
||||
```javascript
|
||||
random.random_whole(10) // 0-9
|
||||
random.random_whole(100) // 0-99
|
||||
random.random_whole(6) + 1 // dice roll: 1-6
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```javascript
|
||||
var random = use('random')
|
||||
|
||||
// Random boolean
|
||||
var coin_flip = random.random() < 0.5
|
||||
|
||||
// Random element from array
|
||||
function pick(arr) {
|
||||
return arr[random.random_whole(length(arr))]
|
||||
}
|
||||
|
||||
var colors = ["red", "green", "blue"]
|
||||
var color = pick(colors)
|
||||
|
||||
// Shuffle array
|
||||
function shuffle(arr) {
|
||||
var result = array(arr) // copy
|
||||
for (var i = length(result) - 1; i > 0; i--) {
|
||||
var j = random.random_whole(i + 1)
|
||||
var temp = result[i]
|
||||
result[i] = result[j]
|
||||
result[j] = temp
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Random in range
|
||||
function random_range(min, max) {
|
||||
return min + random.random() * (max - min)
|
||||
}
|
||||
|
||||
var x = random_range(-10, 10) // -10 to 10
|
||||
```
|
||||
123
docs/library/text.md
Normal file
123
docs/library/text.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# text
|
||||
|
||||
The `text` function and its methods handle string conversion and manipulation.
|
||||
|
||||
## Conversion
|
||||
|
||||
### text(array, separator)
|
||||
|
||||
Convert an array to text, joining elements with a separator (default: space).
|
||||
|
||||
```javascript
|
||||
text([1, 2, 3]) // "1 2 3"
|
||||
text([1, 2, 3], ", ") // "1, 2, 3"
|
||||
text(["a", "b"], "-") // "a-b"
|
||||
```
|
||||
|
||||
### text(number, radix)
|
||||
|
||||
Convert a number to text. Radix is 2-36 (default: 10).
|
||||
|
||||
```javascript
|
||||
text(255) // "255"
|
||||
text(255, 16) // "ff"
|
||||
text(255, 2) // "11111111"
|
||||
```
|
||||
|
||||
### text(text, from, to)
|
||||
|
||||
Extract a substring from index `from` to `to`.
|
||||
|
||||
```javascript
|
||||
text("hello world", 0, 5) // "hello"
|
||||
text("hello world", 6) // "world"
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### text.lower(text)
|
||||
|
||||
Convert to lowercase.
|
||||
|
||||
```javascript
|
||||
text.lower("HELLO") // "hello"
|
||||
```
|
||||
|
||||
### text.upper(text)
|
||||
|
||||
Convert to uppercase.
|
||||
|
||||
```javascript
|
||||
text.upper("hello") // "HELLO"
|
||||
```
|
||||
|
||||
### text.trim(text, reject)
|
||||
|
||||
Remove characters from both ends. Default removes whitespace.
|
||||
|
||||
```javascript
|
||||
text.trim(" hello ") // "hello"
|
||||
text.trim("xxhelloxx", "x") // "hello"
|
||||
```
|
||||
|
||||
### text.search(text, target, from)
|
||||
|
||||
Find the position of `target` in `text`. Returns `null` if not found.
|
||||
|
||||
```javascript
|
||||
text.search("hello world", "world") // 6
|
||||
text.search("hello world", "xyz") // null
|
||||
text.search("hello hello", "hello", 1) // 6
|
||||
```
|
||||
|
||||
### text.replace(text, target, replacement, limit)
|
||||
|
||||
Replace occurrences of `target` with `replacement`.
|
||||
|
||||
```javascript
|
||||
text.replace("hello", "l", "L") // "heLLo"
|
||||
text.replace("hello", "l", "L", 1) // "heLlo"
|
||||
|
||||
// With function
|
||||
text.replace("hello", "l", function(match, pos) {
|
||||
return pos == 2 ? "L" : match
|
||||
}) // "heLlo"
|
||||
```
|
||||
|
||||
### text.format(text, collection, transformer)
|
||||
|
||||
Substitute `{key}` placeholders with values from a collection.
|
||||
|
||||
```javascript
|
||||
text.format("Hello, {name}!", {name: "World"})
|
||||
// "Hello, World!"
|
||||
|
||||
text.format("{0} + {1} = {2}", [1, 2, 3])
|
||||
// "1 + 2 = 3"
|
||||
```
|
||||
|
||||
### text.normalize(text)
|
||||
|
||||
Unicode normalize the text (NFC form).
|
||||
|
||||
```javascript
|
||||
text.normalize("café") // normalized form
|
||||
```
|
||||
|
||||
### text.codepoint(text)
|
||||
|
||||
Get the Unicode codepoint of the first character.
|
||||
|
||||
```javascript
|
||||
text.codepoint("A") // 65
|
||||
text.codepoint("😀") // 128512
|
||||
```
|
||||
|
||||
### text.extract(text, pattern, from, to)
|
||||
|
||||
Match a pattern and extract named groups.
|
||||
|
||||
```javascript
|
||||
text.extract("2024-01-15", /(\d+)-(\d+)-(\d+)/)
|
||||
// Returns match info
|
||||
```
|
||||
116
docs/library/time.md
Normal file
116
docs/library/time.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# time
|
||||
|
||||
The time module provides time constants and conversion functions.
|
||||
|
||||
```javascript
|
||||
var time = use('time')
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
| Constant | Value | Description |
|
||||
|----------|-------|-------------|
|
||||
| `time.second` | 1 | Seconds in a second |
|
||||
| `time.minute` | 60 | Seconds in a minute |
|
||||
| `time.hour` | 3600 | Seconds in an hour |
|
||||
| `time.day` | 86400 | Seconds in a day |
|
||||
| `time.week` | 604800 | Seconds in a week |
|
||||
| `time.month` | 2629746 | Seconds in a month (30.44 days) |
|
||||
| `time.year` | 31556952 | Seconds in a year (365.24 days) |
|
||||
|
||||
## Getting Current Time
|
||||
|
||||
### time.number()
|
||||
|
||||
Get current time as seconds since epoch.
|
||||
|
||||
```javascript
|
||||
var now = time.number() // e.g., 1702656000
|
||||
```
|
||||
|
||||
### time.record()
|
||||
|
||||
Get current time as a record.
|
||||
|
||||
```javascript
|
||||
var now = time.record()
|
||||
// {year: 2024, month: 1, day: 15, hour: 10, minute: 30, second: 45, nanosecond: 123456789}
|
||||
```
|
||||
|
||||
### time.text(format)
|
||||
|
||||
Get current time as formatted text.
|
||||
|
||||
```javascript
|
||||
time.text() // default format
|
||||
time.text("yyyy-MM-dd HH:mm:ss.SSS") // custom format
|
||||
```
|
||||
|
||||
## Converting Time
|
||||
|
||||
### time.number(text, format, zone)
|
||||
|
||||
Parse text to timestamp.
|
||||
|
||||
```javascript
|
||||
time.number("2024-01-15", "yyyy-MM-dd")
|
||||
```
|
||||
|
||||
### time.number(record)
|
||||
|
||||
Convert record to timestamp.
|
||||
|
||||
```javascript
|
||||
time.number({year: 2024, month: 1, day: 15})
|
||||
```
|
||||
|
||||
### time.text(number, format, zone)
|
||||
|
||||
Format timestamp as text.
|
||||
|
||||
```javascript
|
||||
time.text(1702656000, "yyyy-MM-dd") // "2024-01-15"
|
||||
```
|
||||
|
||||
### time.record(number)
|
||||
|
||||
Convert timestamp to record.
|
||||
|
||||
```javascript
|
||||
time.record(1702656000)
|
||||
// {year: 2024, month: 1, day: 15, ...}
|
||||
```
|
||||
|
||||
## Time Arithmetic
|
||||
|
||||
```javascript
|
||||
var now = time.number()
|
||||
|
||||
// Tomorrow at this time
|
||||
var tomorrow = now + time.day
|
||||
|
||||
// One week ago
|
||||
var last_week = now - time.week
|
||||
|
||||
// In 2 hours
|
||||
var later = now + (2 * time.hour)
|
||||
|
||||
// Format future time
|
||||
log.console(time.text(tomorrow))
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
var time = use('time')
|
||||
|
||||
// Measure execution time
|
||||
var start = time.number()
|
||||
// ... do work ...
|
||||
var elapsed = time.number() - start
|
||||
log.console(`Took ${elapsed} seconds`)
|
||||
|
||||
// Schedule for tomorrow
|
||||
var tomorrow = time.number() + time.day
|
||||
log.console(`Tomorrow: ${time.text(tomorrow, "yyyy-MM-dd")}`)
|
||||
```
|
||||
193
docs/packages.md
Normal file
193
docs/packages.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Packages
|
||||
|
||||
Packages are the fundamental unit of code organization and sharing in Cell.
|
||||
|
||||
## Package Structure
|
||||
|
||||
A package is a directory containing a `cell.toml` manifest:
|
||||
|
||||
```
|
||||
mypackage/
|
||||
├── cell.toml # package manifest
|
||||
├── main.ce # entry point (optional)
|
||||
├── utils.cm # module
|
||||
├── helper/
|
||||
│ └── math.cm # nested module
|
||||
├── render.c # C extension
|
||||
└── _internal.cm # private module (underscore prefix)
|
||||
```
|
||||
|
||||
## cell.toml
|
||||
|
||||
The package manifest declares metadata and dependencies:
|
||||
|
||||
```toml
|
||||
package = "mypackage"
|
||||
version = "1.0.0"
|
||||
|
||||
[dependencies]
|
||||
prosperon = "gitea.pockle.world/john/prosperon"
|
||||
mylib = "/Users/john/work/mylib"
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
- **package** — canonical package name
|
||||
- **version** — semantic version
|
||||
- **dependencies** — map of alias to package locator
|
||||
|
||||
## Module Resolution
|
||||
|
||||
When importing with `use()`, Cell searches in order:
|
||||
|
||||
1. **Local package** — relative to package root
|
||||
2. **Dependencies** — via aliases in `cell.toml`
|
||||
3. **Core** — built-in Cell modules
|
||||
|
||||
```javascript
|
||||
// In package 'myapp' with dependency: renderer = "gitea.pockle.world/john/renderer"
|
||||
|
||||
use('utils') // myapp/utils.cm
|
||||
use('helper/math') // myapp/helper/math.cm
|
||||
use('renderer/sprite') // renderer package, sprite.cm
|
||||
use('json') // core module
|
||||
```
|
||||
|
||||
### Private Modules
|
||||
|
||||
Files starting with underscore are private:
|
||||
|
||||
```javascript
|
||||
// _internal.cm is only accessible within the same package
|
||||
use('internal') // OK from same package
|
||||
use('myapp/internal') // Error from other packages
|
||||
```
|
||||
|
||||
## Package Locators
|
||||
|
||||
### Remote Packages
|
||||
|
||||
Hosted on Gitea servers:
|
||||
|
||||
```
|
||||
gitea.pockle.world/user/repo
|
||||
```
|
||||
|
||||
### Local Packages
|
||||
|
||||
Absolute filesystem paths:
|
||||
|
||||
```
|
||||
/Users/john/work/mylib
|
||||
```
|
||||
|
||||
Local packages are symlinked into the shop, making development seamless.
|
||||
|
||||
## The Shop
|
||||
|
||||
Cell stores all packages in the **shop** at `~/.cell/`:
|
||||
|
||||
```
|
||||
~/.cell/
|
||||
├── packages/
|
||||
│ ├── core -> gitea.pockle.world/john/cell
|
||||
│ ├── gitea.pockle.world/
|
||||
│ │ └── john/
|
||||
│ │ ├── cell/
|
||||
│ │ └── prosperon/
|
||||
│ └── Users/
|
||||
│ └── john/
|
||||
│ └── work/
|
||||
│ └── mylib -> /Users/john/work/mylib
|
||||
├── lib/
|
||||
│ ├── local.dylib
|
||||
│ └── gitea_pockle_world_john_prosperon.dylib
|
||||
├── build/
|
||||
│ └── <content-addressed cache>
|
||||
├── cache/
|
||||
│ └── <downloaded zips>
|
||||
├── lock.toml
|
||||
└── link.toml
|
||||
```
|
||||
|
||||
### lock.toml
|
||||
|
||||
Tracks installed package versions:
|
||||
|
||||
```toml
|
||||
[gitea.pockle.world/john/prosperon]
|
||||
type = "gitea"
|
||||
commit = "abc123..."
|
||||
updated = 1702656000
|
||||
```
|
||||
|
||||
### link.toml
|
||||
|
||||
Development links override package resolution:
|
||||
|
||||
```toml
|
||||
[gitea.pockle.world/john/prosperon]
|
||||
target = "/Users/john/work/prosperon"
|
||||
```
|
||||
|
||||
## Installing Packages
|
||||
|
||||
```bash
|
||||
# Install from remote
|
||||
cell install gitea.pockle.world/john/prosperon
|
||||
|
||||
# Install from local path
|
||||
cell install /Users/john/work/mylib
|
||||
```
|
||||
|
||||
## Updating Packages
|
||||
|
||||
```bash
|
||||
# Update all
|
||||
cell update
|
||||
|
||||
# Update specific package
|
||||
cell update gitea.pockle.world/john/prosperon
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
For active development, link packages locally:
|
||||
|
||||
```bash
|
||||
# Link a package for development
|
||||
cell link add gitea.pockle.world/john/prosperon /Users/john/work/prosperon
|
||||
|
||||
# Changes to /Users/john/work/prosperon are immediately visible
|
||||
|
||||
# Remove link when done
|
||||
cell link delete gitea.pockle.world/john/prosperon
|
||||
```
|
||||
|
||||
## C Extensions
|
||||
|
||||
C files in a package are compiled into a dynamic library:
|
||||
|
||||
```
|
||||
mypackage/
|
||||
├── cell.toml
|
||||
├── render.c # compiled to mypackage.dylib
|
||||
└── render.cm # optional Cell wrapper
|
||||
```
|
||||
|
||||
The library is named after the package and placed in `~/.cell/lib/`.
|
||||
|
||||
See [Writing C Modules](c-modules.md) for details.
|
||||
|
||||
## Platform-Specific Files
|
||||
|
||||
Use suffixes for platform-specific implementations:
|
||||
|
||||
```
|
||||
mypackage/
|
||||
├── audio.c # default implementation
|
||||
├── audio_playdate.c # Playdate-specific
|
||||
└── audio_emscripten.c # Web-specific
|
||||
```
|
||||
|
||||
Cell selects the appropriate file based on the build target.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Programs: Programs and Modules
|
||||
|
||||
Prosperon organizes your code into two broad categories: **modules** and **programs**. Modules are used to extend programs with new functionality, while programs are used to spawn actors.
|
||||
|
||||
## Modules
|
||||
|
||||
A **module** is any file that returns a single value. This return value is commonly an object, but it can be any data type (string, number, function, etc.). Once a module returns its value, Prosperon **freezes** that value, preventing accidental modification. The module is then cached so that subsequent imports of the same module don’t re-run the file—they reuse the cached result.
|
||||
|
||||
### Importing a Module
|
||||
|
||||
Use the built-in `use` function to import a module by file path (or by name if resolvable via Prosperon’s path settings). For example:
|
||||
|
||||
```
|
||||
var myModule = use('scripts/modules/myModule')
|
||||
```
|
||||
|
||||
`use('module')` returns the **exact** same object if called multiple times, since modules are cached and not re-run.
|
||||
|
||||
Dull based modules are resolved by searching for them from the `prosperon.PATH` array. Engine modules are stored under `scripts/modules`, which is already added to the PATH for you.
|
||||
|
||||
Prosperon can also load C based modules. If two modules have the same path resolution, the C based library will be imported.
|
||||
|
||||
## Programs
|
||||
|
||||
An **program** is a file that **does not** return a value. Instead, the file’s contents run top to bottom as soon as the program is spawned. Programs are your game’s “live” scripts: each program can hold its own state and logic, spawn sub-programs, schedule timed tasks, and eventually **kill** itself (or be killed) when it’s done.
|
||||
|
||||
### Program Intrinsic Functions
|
||||
|
||||
Certain functions are intrinsic to the program and cannot be overridden. They’re assigned to each new program instance at spawn time:
|
||||
|
||||
1. **`spawn(script, config, callback)`**
|
||||
Creates (spawns) a new program from another script file.
|
||||
- **`script`**: Path to the program script (a file containing statements, not returning anything).
|
||||
- **`config`**: Optional object of extra properties to assign to the new program.
|
||||
- **`callback(underling, info)`**: Optional function invoked right after the program is instantiated but before it fully initializes.
|
||||
|
||||
The newly spawned program:
|
||||
- Receives a reference to its parent (the `overling`) and can store child programs (the `underlings`).
|
||||
- Automatically calls `awake()` if that function is defined, after basic setup completes.
|
||||
- Registers any recognized event handlers (like `update`, `draw`, etc.) if they exist.
|
||||
|
||||
2. **`kill()`**
|
||||
Destroys the program, all of its timers, and recursively kills any underling (child) programs. If the program has a parent, it is removed from the parent’s `underlings` set.
|
||||
|
||||
3. **`delay(fn, seconds)`**
|
||||
Runs the given function `fn` after `seconds`. This is implemented under the hood with a timer that automatically clears itself once it fires.
|
||||
- **Example**:
|
||||
```js
|
||||
this.delay(_ => {
|
||||
log.console("3 seconds later!")
|
||||
}, 3)
|
||||
```
|
||||
|
||||
4. **`clear()`**
|
||||
Recursively kills all child programs, clearing your immediate `underlings` set. This is not called automatically. You can use it to manually clean up all children without necessarily killing the program itself.
|
||||
|
||||
### The program Lifecycle
|
||||
|
||||
Specific hooks can be set on a program when it is initialized.
|
||||
|
||||
- **Awake**: If the new program defines `awake()`, Prosperon calls it after the script finishes its top-level execution. This is a common place to do initialization.
|
||||
- **Garbage**: When the program is killed, if it has a `garbage()` function, Prosperon calls it before final removal.
|
||||
- **Then**: If the program has a `then()` function, Prosperon calls it at the very end of the kill process, allowing any final statements after your `garbage()` logic completes.
|
||||
- **Registration**: In addition, if the object has **any** function named the same thing as a hook created with **prosperon.on**, that function will be registered with it after initialization.
|
||||
|
||||
### Overlings and Underlings
|
||||
|
||||
Programs have access to its creator and other programs created underneath it, termed its overling and underlings.
|
||||
|
||||
- **`this.overling`** is the parent program that spawned the current one.
|
||||
- **`this.underlings`** is a set of child programs that the current program has spawned.
|
||||
|
||||
Killing a parent automatically kills all of its underlings, which in turn can kill their own underlings, and so on.
|
||||
|
||||
## Program Documentation
|
||||
|
||||
Prosperon includes a module called `doc.js` which helps generate documentation for your modules and programs. Any function and value can be assigned a docstring, and prosperon will then be able to generate documentation for it via doc.js. Look under the module API for more info.
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Prosperon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>pockle.world.prosperon</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Prosperon</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.5</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2024 Pockle World. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
164
help/cell.md
164
help/cell.md
@@ -1,164 +0,0 @@
|
||||
JAVASCRIPT VISION
|
||||
|
||||
I see objects as being a sort of combination of a lisp cell and a record: symbols, which are used internally, and are private and non iterable, and record string values, which are iterable, readable, and writable; of course everything becomes locked in when stone.
|
||||
|
||||
CELLSCRIPT
|
||||
|
||||
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.
|
||||
|
||||
## Actors and Objects
|
||||
Actors have a unique memory space and are made up of many objects. Objects are created in the Self style, but with a limitation: only one parent.
|
||||
|
||||
Actors only communicate with messages. Messages are a record of data consisting of a few base types: text, numbers, arrays, records, boolean values. There is no RPC, and it is not recommended to build it into your message passing protocol. Messages are very high level things: "do X", which the actor can then go and carry out.
|
||||
|
||||
Cell provides a fast way to condense an object for sending.
|
||||
|
||||
## How is it different from Javascript?
|
||||
Cell condenses Javascript down into a few core ideas. There are three pillars which cell relies on:
|
||||
|
||||
1. The idea of actors as a method of communication between parts of a program.
|
||||
2. The idea of objects as a way to organize and encapsulate data.
|
||||
3. The idea of the capability model as security.
|
||||
|
||||
Javascript already supplied some of these things; Cell takes the core of Javascript and makes these ideas more explicit, and layers on the actor communication. It removes some goofy suckiness with javascript.
|
||||
|
||||
It acts as something like an operating system at the application level. It allows random code to be ran on your machine without worrying it will break something. This is built into the language.
|
||||
|
||||
It is completly dynamically typed. In comparison with C, in C, you can treat everything as everything: it is almost not typed at all. If you try to use a type as another type, no error is thrown; it might work, but it mightly silently not work. In Cell, data has a hard type, but if you use it "incorrectly", it will throw, and you can correct it. It's a live system.
|
||||
|
||||
Cell is linked very closely with C. It's best to think of cell as a layer for message passing on top of C. It is a way to describe how to translate C tasks from one section of the program to another - or to totally different computers (actors).
|
||||
|
||||
As such, cell's primary duty is marshalling data; so it has been designed for that to be as fast as possible. It has a syntax similar to C to make it easy to translate formulae from cell to C (or the other way, if desired).
|
||||
|
||||
Unlike many actor languages, Cell does not eschew assignment. You must have some assignment. However, when it comes to actor->actor communication, you do not assign. RPC is too direct: one actor should not care all that much what specific functions another actor has available. It should request it to do something, and get a result, or possibly not get a result. It doesn't care what the actor does as long as that gets done.
|
||||
|
||||
But within itself, it will assign; it must. Actors, or cells, are best thought of as computers or nodes within the internet. You request data from a URL by typing it into your browser; that computer you're attempting to reach may not even be on. It very likely has written some other data to disk whenever you contact it. But you're not doing the specific assigning. You just request data with HTTP commands.
|
||||
|
||||
## Objects and actors
|
||||
Objects and actors are both similar ideas: they can hold data and respond to messages. Objects, local to an actor, can be thought of more like an RPC idea: they're invoked and return immediately. However, a failed RPC can crash an object; and in that case, the actor halts. It can be corrected.
|
||||
|
||||
## What does Cell bring you over C?
|
||||
Programs which are built with C; they're built statically; they're built to not crash; they're built doing extremely low level things, like assignment.
|
||||
|
||||
The goal of cell is to thrust your C code into the parallel, actor realm. It lets your code crash and resume it; even rewriting the C code which is butressing your cell code and reloading it live.
|
||||
|
||||
There are two primary sorts of Cell modules you create from C code: data and IO. C code like
|
||||
|
||||
Where there were two similar things in javscript, one has been deleted and one kept. For example, there is only null now, no undefined. There are not four ways to test for equality; there is one.
|
||||
|
||||
The purpose of this is to be a great language for passing messages. So it should be fast at creating records first and foremost, and finding items on them. So it needs first class, jitt'd records.
|
||||
|
||||
Finally, it needs to use less memory. Deleting a bunch of this stuff should make that simpler.
|
||||
|
||||
What is present?
|
||||
Objects, prototypes, numbers, arrays, strings, true, false, null.
|
||||
|
||||
Things to do:
|
||||
|
||||
merge typeof and instanceof. Misty has array? stone? number? etc; it needs to be generic. 5 is number returns true.
|
||||
|
||||
No new operator. It's the same idea though: simply instead of 'var guy = new sprite({x,y})' you would say 'var guy = sprite({x,y})', and sprite would simply be a function written to return a sprite object.
|
||||
|
||||
One number type. Dec64. Numeric stack can be added in later: a bigint library, for example, built inside cell.
|
||||
|
||||
Simplify the property attributes stuff. It is simple: objects have text keys and whatever values. Objects can also have objects as values. These work like symbols. You can share them, if desired. No well known symbols exist to eliminate that much misdirection. Obejcts basically work like private keys. If you serialize an object, objects that are keys are not serialized; only textual keys are. You can do something about it with a json() method that is invoked, if you desire. You cannot retrieve
|
||||
|
||||
var works like let; use var instead of let
|
||||
|
||||
no const
|
||||
|
||||
Function closures and _ => all work the same and close over the 'this' variable
|
||||
|
||||
Totally delete modules, coroutines, generators, proxy .. this deletes a lot of the big switch statement
|
||||
|
||||
Add the 'go' statement for tail calls
|
||||
|
||||
Add the 'do' statement
|
||||
|
||||
Implementation detail: separate out arrays and objects. They are not the same. Objects no longer need to track if they're fast arrays or not. They're not. Arrays are. Always.
|
||||
|
||||
Add the functional proxy idea. Log will be implemented through that.
|
||||
|
||||
Remove ===; it's just == now, and !=.
|
||||
|
||||
Remove 'continue'; now, break handles both. For a do statement, label it, and break to that label; so
|
||||
|
||||
var x = 0
|
||||
do loop {
|
||||
x++
|
||||
if (x < 5) break loop // goes back to loop
|
||||
break // exits loop
|
||||
}
|
||||
|
||||
rename instanceof to 'is'
|
||||
|
||||
remove undefined; all are 'null' now
|
||||
|
||||
remove 'delete'; to remove a field, assign it to null
|
||||
|
||||
remove with
|
||||
|
||||
Remove Object. New records have a prototype of nothing. There are no more 'type prototypes' at all.
|
||||
|
||||
Arrays are their own type
|
||||
|
||||
Remove property descriptors. Properties are always settable, unless the object as a whole is stone. Stone is an object property instead of a shape property.
|
||||
|
||||
Syntax stuff .. would like to invoke functions without (). This can effectively simulate a "getter". Make ? and all other characters usable for names. No reserve words, which are endlessly irritating.
|
||||
|
||||
----
|
||||
|
||||
This will all actually come about gradually. Add a few things at a time, fix up code that did not adhere. For a lot of this, no new functions will even need to be written; it's a matter of not calling certain functions that are no longer relevant, or calling different functions when required.
|
||||
|
||||
|
||||
## Benchmarks to implement
|
||||
### general speed
|
||||
binarytrees
|
||||
coro-prime-sieve
|
||||
edigits
|
||||
fannkuch-redux
|
||||
fasta
|
||||
http-server
|
||||
json serialize/deserialize
|
||||
knucleotide
|
||||
lru
|
||||
mandelbrot
|
||||
merkletrees
|
||||
nbody
|
||||
nsieve
|
||||
pidigits
|
||||
regex-redux
|
||||
secp256k1
|
||||
spectral-norm
|
||||
|
||||
### function calling and recursion stress - test goto
|
||||
naive recursive fibonacci [fib(35) or fib(40)]
|
||||
tak
|
||||
ackermann
|
||||
|
||||
### numeric
|
||||
sieve of eratosthenes [10^7 bits]
|
||||
spectral norm [5500 x 5500 matrix]
|
||||
n-body sim [50 000 - 100 000 steps]
|
||||
mandelbrot [1600x1200 image, max iter = 50]
|
||||
|
||||
### memory & gc torture
|
||||
binary trees [depth 18 (~500 000 nodes)]
|
||||
richards task scheduler
|
||||
fannkuch redux [n=11 or 12]
|
||||
|
||||
### dynamic object & property access
|
||||
deltablue constraint solver
|
||||
splay tree [256k nodes]
|
||||
json, wota, nota decode->encode [use 2MB example]
|
||||
|
||||
### string / regex kernels
|
||||
regex-DNA
|
||||
fasta
|
||||
word-frequency
|
||||
|
||||
### concurrency/message passing
|
||||
ping-pong [two actors exhange a small record N times, 1M messages end to end]
|
||||
chameneos [mating color swap game w/ randezvous]
|
||||
|
||||
For all, track memory and time.
|
||||
@@ -1,57 +0,0 @@
|
||||
# Cell
|
||||
Cell is an actor system, intended to make it easy to parallelize programs.
|
||||
|
||||
Programs are written in cell, but cell also makes it trivial to interface with C code.
|
||||
|
||||
## Packages
|
||||
Each cell package has a .cell directory at its root. .ce files within this directory can be launched as cell actors, and .cm files can be imported with the 'use' statement.
|
||||
|
||||
### File search
|
||||
When a file is imported, cell will search the following locations:
|
||||
- The current package
|
||||
- Imported packages
|
||||
- The cell standard library
|
||||
|
||||
Files leading with an underscore are not available to import from a package (but are OK for within a package)
|
||||
|
||||
For example, consider this project:
|
||||
prosperon/
|
||||
prosperon/sprite.cm
|
||||
prosperon/render.cm
|
||||
prosperon/_help.cm
|
||||
prosperon/render.c
|
||||
prosperon/ui/
|
||||
prosperon/ui/button.cm
|
||||
|
||||
sprite.cm can use(render); but an outside package must use(prosperon/render). sprite can use(help), but an outside package cannot use(prosperon/help).
|
||||
|
||||
A file like render.c above will be compiled into a dynamic library for the target platform (ie, on macos a .dylib), named the name of the package, 'prosperon.dylib'. For a local package, it will be local.dylib.
|
||||
|
||||
In the case above, the js_prosperon_render_use function will be found in the prosperon.dylib, and the result of that will be passed to render.cm as 'this', allowing for trivial extension.
|
||||
|
||||
If a C function OR a .cm file is found for a particular import, searching stops, preventing, for example, accidentally applying a .cm file from a local find to a package that has one which is similarly named.
|
||||
|
||||
### Importing packages
|
||||
Each package has a list of packages it depends on. They can use their own naming convention for them. Packages are imported from URLs, so, gitea.pockle.world/john/prosperon imports 'prosperon'; but, when importing, you can say, 'renderer = gitea.pockle.world/john/prosperon', and that means you can use(renderer/sprite), etc.
|
||||
|
||||
## Modules
|
||||
Modules have a .cm extension. Modules return a single value.
|
||||
|
||||
## Programs
|
||||
Programs have a .ce extension. Programs do not return a value.
|
||||
|
||||
## The scripting language
|
||||
Cell is basically javascript, but with some key modifications:
|
||||
- blobs instead of arraybuffers, which are written and read bit by bit
|
||||
- All equivalences are hard; there is only != and ==, and they do what !== and === do in javascript
|
||||
- There is no undefined, only null
|
||||
- 'var' acts like let, and 'def' acts like const; there is no let and const
|
||||
- logging is done with the global 'log' object; log.console outputs to console, log.error outputs an error, etc.
|
||||
- All numbers are dec64, and are all therefore exact; no floating point rounding errors.
|
||||
- All closures act like _ =>, and close over the 'this' variable
|
||||
- Arrays are distinct from objects
|
||||
- There is no 'delete' operator, just assign to null
|
||||
- There is no 'with'
|
||||
- There are no property descriptors, objects are more like records that can hold functions, numbers, anything.
|
||||
- stone() makes an object immutable, forever
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# Cell structure
|
||||
|
||||
Cell doesn't have the notion of a file system; it has packages. Each package is a directory with a cell.toml file. You cannot have nested packages.
|
||||
|
||||
A package is a collection of actors, modules, and other files.
|
||||
|
||||
Packages are stored in a cell shop, in a development environment, located at ~/.cell.
|
||||
|
||||
Inside the shop there is .cell/packages/<each package>
|
||||
|
||||
A package can be a gitea url, like gitea.pockle.world/john/prosperon, which effectively clones to ~/.cell/packages/gitea.pockle.world/john/prosperon
|
||||
|
||||
Or it can be a local absolute path (on computers with a file system), like .cell/packages/User/john/work/prosperon, which is a symlink to the actual directory.
|
||||
|
||||
Cell itself is stored as a package in .cell/packages/<core>, where <core> is specified in shop.toml (e.g., `core = "bootstrap"` for local development, or `core = "gitea.pockle.world/john/cell"` for a remote package). Updating cell involves getting cell from somewhere, and rebuilding it. The cell core is itself a package.
|
||||
|
||||
When an actor or module is requested, it's loaded first from the package, if not found, it checks in packages (via aliases in cell.toml dependencies), and if not found there, it checks in the core package.
|
||||
|
||||
Packages can declare aliases for packages, so the "accio" package can say "prosperon = gitea.pockle.world/john/prosperon", and then load "prosperon/sprite".
|
||||
A module in prosperon can simply load "sprite" to load its own sprite.
|
||||
|
||||
the "cell upgrade" command is a command to upgrade the cell version in a shop.
|
||||
|
||||
## commands
|
||||
cell update - update all packages in the shop, and builds their code for local use
|
||||
cell install <package> - install a package
|
||||
cell remove <package> - remove a package
|
||||
cell build - build the package in the current directory into a cell executable
|
||||
cell version - the version of cell being used
|
||||
|
||||
## building
|
||||
cell builds itself from source on your compuer!
|
||||
|
||||
all source for every single part of a cell program are located in the cell shop.
|
||||
|
||||
the cell shop looks like this:
|
||||
.cell
|
||||
packages
|
||||
core <--- this is the core cell
|
||||
gitea.pockle.world/john/cell <---- core contents. this is linked to core
|
||||
gitea.pockle.world/john/prosperon
|
||||
cell.toml <--- the manifest of the package
|
||||
mod1.cm
|
||||
mod2.cm
|
||||
actor1.ce
|
||||
addon.c <--- a C file for compiling into the cell executable
|
||||
etc
|
||||
gitea.pockle.world/john/accio
|
||||
/Users/john/work/prosperon
|
||||
...
|
||||
link.toml - temporary links for this cell shop
|
||||
lock.toml - manifest of installed packages
|
||||
lib
|
||||
<dynamic libraries>
|
||||
cache
|
||||
<downloaded packages>
|
||||
build - content addressed hash
|
||||
fajfola214o12
|
||||
90823tjoag
|
||||
...
|
||||
|
||||
## getting a module or actor
|
||||
When a module <name/mod> is requested, from a within package <a> ..
|
||||
|
||||
1) cell looks within package <a> for a folder <name> and loads <name/mod>, if present
|
||||
2) cell looks for a package <name> with a top level <mod>
|
||||
3) cell looks in the core for <name/mod>
|
||||
|
||||
The core can be set and have something else linked into it, if you want to break your cell build
|
||||
|
||||
## cell build
|
||||
cell build compiles all c into the appropriate binaries. All object files are stored in the .cell/cache
|
||||
|
||||
there are two ways to build a cell program: as a shared library, or as a static binary.
|
||||
|
||||
Cell script files are simply compiled into built objects
|
||||
|
||||
Then, all C files in the package are compiled.
|
||||
|
||||
If you have a file like fd.c and fd_playdate.c, that is a signal to cell to compile fd.c usually, but then for the playdate target, compile fd_playdate.c instead.
|
||||
|
||||
files name "main.c" are not compiled.
|
||||
|
||||
each cell.toml in a package
|
||||
|
||||
### shared library
|
||||
this is more suitable for development. firstly, the cell core must build a shared library for the platform.
|
||||
|
||||
Then, each package compiles, linking to the cell core shared library. Modules are written to include the cell core headers.
|
||||
|
||||
Shared libraries are stored in the build directory, hashed with the content hash
|
||||
|
||||
When update is run, it copies the dynamic libraries into .cell/shared, with their package name; ie, the dynamic library for the gitea.pockle.world/john/prosperon would be at .cell/shared/gitea_pockle_world_john_prosperon.dylib.
|
||||
|
||||
There will be commands to gather them all together; so you can request "all dylibs for platform x", and it will give you a folder of them.
|
||||
|
||||
### bootstrapping cell
|
||||
after the cell shared libraries are all compiled, the main.c of the cell core is compiled against the cell core shared library, creating a thin cell runner. However, even on subsequent updates to cell, this runner is not updated; only for bootstrapping.
|
||||
|
||||
### static binary
|
||||
For static binaries, rather than going through and compiling each dynamic library for a package, a static binary is created for a particular package, based on its dependencies. All C symbols, plus the main.c runner from the cell core, are packaged into one binary. It works just like the basic cell program.
|
||||
|
||||
However, you can also specify an entry point for this binary, for example, a particular actor. When the binary is run, that particular actor runs.
|
||||
@@ -1,32 +0,0 @@
|
||||
Prosperon Commercial License
|
||||
Last Updated: February 11, 2025
|
||||
|
||||
Grant of License
|
||||
Upon purchasing a Prosperon license, Pockle World LLC (“Licensor”) grants you (“Licensee”) a non-exclusive, worldwide, commercial license to use the Prosperon game engine (the “Software”) to develop, publish, and distribute one or more video game or interactive software products (the “Products”).
|
||||
|
||||
Closed-Source Restriction
|
||||
The Software is currently closed-source. Licensee shall not redistribute or disclose the Prosperon engine source code, in whole or in part, unless expressly permitted by Pockle World LLC.
|
||||
|
||||
Permitted Usage
|
||||
Licensee may create and distribute compiled or packaged Products that incorporate or depend upon the Software, for commercial or non-commercial purposes, without owing royalties or additional fees to Licensor (beyond the initial purchase).
|
||||
|
||||
Ownership and Copyright
|
||||
The Software is owned by Pockle World LLC. All rights not expressly granted in this License are reserved by Pockle World LLC.
|
||||
|
||||
Modifications
|
||||
You may modify the scripts of your own Projects, but you may not share or distribute any modifications to the Prosperon engine’s proprietary binaries, libraries, or source code itself.
|
||||
|
||||
No Warranty
|
||||
THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
|
||||
Limitation of Liability
|
||||
IN NO EVENT SHALL LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
|
||||
|
||||
Term and Termination
|
||||
This License shall remain in effect perpetually, unless you fail to comply with any term of this License. In such event, your rights under this License will terminate immediately without notice from Pockle World LLC.
|
||||
|
||||
Support and Updates
|
||||
Purchase of the Software includes access to all updates through Prosperon v1.X. Additional support plans may be purchased separately.
|
||||
|
||||
Governing Law
|
||||
This License shall be governed by and construed in accordance with the laws of the jurisdiction in which Pockle World LLC is located, without regard to conflict-of-law principles.
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
## Hot Reloading
|
||||
Hot reloading is of primary concern here. Cell does its best to hot reload modules when possible.
|
||||
|
||||
The key function can be used to create a stable key for a module, which can be used to hot reload modules and aid in data migration.
|
||||
|
||||
During hot reload ...
|
||||
|
||||
All var declarations are rebound, so old var -> new value
|
||||
Def declarations are not rebound, but properties of objects may be adjusted.
|
||||
Returned objects and functions are wrapped in a trampoline and adjusted. The original export shape is stable (same set of keys); you will get a warning if the shape changes, and you must reload the program to get the correct new shape.
|
||||
|
||||
Hot-reload binding rules
|
||||
var bindings are rebound on hot reload
|
||||
New evaluation replaces the old value.
|
||||
Use var for:
|
||||
tunables / config
|
||||
behavior references
|
||||
caches you’re happy to discard
|
||||
anything you expect to change when code changes
|
||||
def bindings are never rebound
|
||||
The binding identity persists across reloads.
|
||||
Use def for:
|
||||
long-lived state
|
||||
prototypes / identity anchors
|
||||
capability keys
|
||||
registries you want to keep alive
|
||||
def objects may have their methods patched
|
||||
|
||||
Hot reload is best-effort. You get the full benefit when modules export a stable API object and keep long-lived identity in def (protos/state), while keeping tunables in var and reading them at use-time; caching primitives or function references opts out of live updates.
|
||||
|
||||
A module is “hot-reload friendly” if its export is a function or an object (not a primitive).
|
||||
On reload, the runtime:
|
||||
Re-evaluates the module.
|
||||
Rebinds all vars: old var binding becomes the new value from the new evaluation.
|
||||
pasted
|
||||
Keeps all def bindings (identity does not change), but:
|
||||
patches function-valued fields on def objects in place to match new code (and any other fields you explicitly define as reloadable).
|
||||
pasted
|
||||
Patches the module export handle (runtime-owned, stone-to-userland) so existing importers keep the same identity, but calls/fields can reflect new code.
|
||||
pasted
|
||||
Checks export shape: if the export is an object, its set of text keys must match the previous version; otherwise warn and require full program reload.
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<copyright>Copyright (c) 2024 Pockle World. All Rights Reserved.</copyright>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
name="Prosperon"
|
||||
version="0.4"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Prosperon Game Engine</description>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
@@ -1,16 +0,0 @@
|
||||
101 ICON "icon.ico"
|
||||
102 RT_MANIFEST "resource.manifest"
|
||||
103 VERSIONINFO
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Pockle World"
|
||||
VALUE "FileDescription", "Prosperon Game Engine"
|
||||
VALUE "FileVersion", "0.4.0"
|
||||
VALUE "LegalCopyright", "Copyright 2024 Pockle World."
|
||||
VALUE "ProductVersion", "0.4.0"
|
||||
END
|
||||
END
|
||||
END
|
||||
@@ -1,152 +0,0 @@
|
||||
# Graphics drawing
|
||||
|
||||
## Terminology
|
||||
Texture - a set of bytes on the GPU, not directly accessible
|
||||
Surface - a set of bytes in RAM, modifiable
|
||||
rect - a rectangle of {x,y,width,height}
|
||||
Image - combination of a texture and rect, where the rect defines the UV coordinates on the texture to draw
|
||||
|
||||
# Drawing, cameras, viewports, logical size, and so on
|
||||
|
||||
A camera is a view into the game world. A camera can be "rendered", which means it renders the world, and what it can see in the world. A camera may draw to a surface, or to the main window. Objects in the world will render so that if their position is equal to the camera position, that is in the center of the screen. HUD functions always render so [0,0] is the bottom left of the camera's view.
|
||||
|
||||
Cameras always draw to their own render target. Then, they draw that render target to the framebuffer.
|
||||
|
||||
# COORDINATES
|
||||
Screen coordinates start in the upper left corner at [0,0] and extend to the bottom right, in pixels. Raw mouse coordinates are in these.
|
||||
|
||||
# RENDERING PIPELINE
|
||||
|
||||
In prosperon, you call graphics rendering functions at well defined hook points. These are interleaved as necessary with predefined created objects, like sprites, 3d world models, and so on.
|
||||
|
||||
The engine stores a command buffer. When you issue "draw" commands, they are recorded into the command buffer. These are batched as much as they can be; if there is no significant state change between, the draw commands can be coalesced into one. Then, for each camera, the draw commands are executed.
|
||||
|
||||
# RENDERING COMPONENTS
|
||||
## MATERIALS
|
||||
A material defines the inputs to a shader.
|
||||
|
||||
## PIPELINES
|
||||
Pipelines are how the rendering engine is set up. Switching pipelines can be done for special effects.
|
||||
|
||||
## SPECIAL EFFECTS
|
||||
Sometimes you want a special effect. While there are many objects in prosperon you can create and have the engine handle for you, a special effect typically requires a bit of code.
|
||||
|
||||
# LAYERS
|
||||
All things that draw have a layer. If no layer is set, the implicit layer is "0". Even draw and hud functions have a layer. To draw a draw function on a specific layer, set that function's "layer". ie,
|
||||
|
||||
this.draw = function() { render.rect(); }
|
||||
this.draw.layer = -5;
|
||||
|
||||
Now that layer will draw at the -5 layer.
|
||||
|
||||
# CAMERAS
|
||||
Everything is drawn via cameras. Cameras can draw directly to the screen, or they can draw to an offscreen render target. By default, everything is drawn to all cameras. There will eventually be a tag that lets you filter what is drawn to specifc cameras.
|
||||
|
||||
Cameras have a resolution they draw at, "size".
|
||||
|
||||
## TEXTURES
|
||||
|
||||
Anatomy of rendpering an image render.image(path)
|
||||
Path can be a file like "toad"
|
||||
If this is a gif, this would display the entire range of the animation
|
||||
It can be a frame of animation, like "frog.0"
|
||||
If it's an aseprite, it can have multiple animations, like "frog.walk.0"
|
||||
file^ frame^ idx
|
||||
|
||||
render.image("frog.walk.0",
|
||||
game.image("frog.walk.0") ==> retrieve
|
||||
|
||||
image = {
|
||||
texture: "spritesheet.png",
|
||||
rect: [x,y,w,h],
|
||||
time: 100
|
||||
},
|
||||
|
||||
frames: {
|
||||
toad: {
|
||||
x: 4,
|
||||
y: 5,
|
||||
w: 10,
|
||||
h: 10
|
||||
},
|
||||
frog: {
|
||||
|
||||
walk: [
|
||||
{ texture: spritesheet.png, x: 10, y:10, w:6,h:6, time: 100 },
|
||||
{ texture: spritesheet.png, x:16,y:10,w:6,h:6,time:100} <--- two frame walk animation
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
texture frog {
|
||||
texture: {"frog.png"}, <--- this is the actual thing to send to the gpu
|
||||
x:0,
|
||||
y:0,
|
||||
w:10,
|
||||
h:10
|
||||
},
|
||||
|
||||
|
||||
## RENDER MODES
|
||||
/* rendering modes
|
||||
ps1
|
||||
gouraud
|
||||
diffuse // 16 bit color, 5-5-5
|
||||
7 dynamic lights, 1 ambient
|
||||
textures are affine
|
||||
no vertex skinning
|
||||
256x256 texture max (generally 128x128)
|
||||
320x240, variable up to 640x480
|
||||
|
||||
n64
|
||||
gouraud
|
||||
diffuse
|
||||
combiner // a secondary texture sometimes used to combine
|
||||
7 dynamic lights, 1 ambient
|
||||
320x240, or 640x480
|
||||
|
||||
sega saturn
|
||||
gouraud
|
||||
diffuse
|
||||
320x240 or 640x480
|
||||
|
||||
ps2
|
||||
phong
|
||||
diffuse
|
||||
combiner // second texture for modulation of diffuse
|
||||
combine_mode // int for how to combine
|
||||
|
||||
dreamcast
|
||||
phong
|
||||
diffuse
|
||||
combiner // second texture; could be an environment map, or emboss bump mapping
|
||||
fog
|
||||
640x480
|
||||
640x448, special mode to 1280x1024
|
||||
|
||||
gamecube
|
||||
phong
|
||||
diffuse
|
||||
+7 textures // wow!
|
||||
8 dynamic lights
|
||||
640x480
|
||||
|
||||
*/
|
||||
|
||||
/* meshes
|
||||
position (float3)
|
||||
color (rgba)
|
||||
uv
|
||||
*/
|
||||
|
||||
/* materials, modern pbr
|
||||
any object can act as a "material". The engine expects some standardized things:
|
||||
diffuse - base color texture
|
||||
bump - a normal map for dot3 bump maping used in phong shading
|
||||
height - a grayscale heightmap
|
||||
occlusion - ambient occlusion texture
|
||||
emission - texture for where model emits light
|
||||
bump2 - a second normal map for detail
|
||||
metallic - a metal/smoothness map
|
||||
specular - specular map, alternative for the metallic workflow
|
||||
*/
|
||||
186
help/todo/ops.md
186
help/todo/ops.md
@@ -1,186 +0,0 @@
|
||||
# RENDERING PIPELINE
|
||||
The basic flow for developing graphics here:
|
||||
|
||||
1) develop a render graph
|
||||
2) decide what to draw
|
||||
|
||||
The render graph is the "big idea" of how the data flows through a render; inside the execution, you utilize "what to draw".
|
||||
|
||||
Prosperon provides you with functions to facilitate the creation of rendering pipelines. For example, you could use "shadow_vol" function to create buffer geometry with shadow volume data.
|
||||
|
||||
Unity has a "graphics.rendermesh" function that you can call, and that unity automatically calls for renderer components. It is the same here. But there are a handful of other types to draw, particularly for 2d.
|
||||
|
||||
## 2D
|
||||
### Anatomy of a 2d renderer
|
||||
Traditionally, 2d rendering is a mix of tilemaps and sprites. Today, it is still more cost effective to render tilemaps, but we have a lot more flexibility.
|
||||
|
||||
NES
|
||||
Nes had 1 tilemap and up to 8 sprites per scanline.
|
||||
|
||||
SNES
|
||||
Up to 4 tilemap backgrounds, with priority, and flipping capability. 32 sprites per scanline, and by setting the priority correctly, they could appear behind background layers.
|
||||
|
||||
GB
|
||||
One background layer, 10 sprites per scanline/40 per frame.
|
||||
|
||||
GBA
|
||||
Up to 4 layers, sprites with affine transforms!
|
||||
|
||||
DS
|
||||
Up to 4 layers; many sprites; and a 3d layer!
|
||||
|
||||
Sega saturn
|
||||
This and everything else with generic vertex processing could do as many background layers and sprites as desired. This is what you get with prosperon on most modern computers. For more limited hardware, your options become limited too!
|
||||
|
||||
### Prosperon rendering
|
||||
Layers
|
||||
Every drawable 2d thing has a layer. This is an integer that goes from -9223372036854775808 to 9223372036854775808.
|
||||
|
||||
!!! On hardware that supports only a limited number of layers, this value must go from 0 to (layer #).
|
||||
|
||||
Layer sort
|
||||
Within a layer, objects are sorted based on a given criteria. By default, this is nothing, and the engine may reorder the draws to optimize for performance. Instead, you can choose to sort by their y axis position, for example.
|
||||
|
||||
Parallax
|
||||
Layers can have a defined parallax value, set at the engine level. Anything on that layer will move with the provided parallax. Each layer has an implicit parallax value of "1", which means it moves "as expected". Below 1 makes it move slower (0 makes it not move at all), 2 makes it move twice as fast, etc.
|
||||
|
||||
Tilemaps
|
||||
These are highly efficient and work just like tilemaps on old consoles. When you submit one of these to draw, Prosperon can efficientally cull what can't be seen by the camera. You can have massive levels with these without any concern for performance. A tilemap is all on its own layer.
|
||||
|
||||
Tiles can be flipped; and the entire tilemap can have an affine transformation applied to it.
|
||||
|
||||
Sprites each have their own layer and affine transform. Tilemaps are just like a large sprite.
|
||||
|
||||
In addition to all of this, objects can have a "draw" event, wherein you can issue direct drawing commands like "render.sprite", "render.text", "render.circle", and so on. This can be useful for special effects, like multi draw passes (set stencil -> draw -> revert stencil). In this case, it is the draw event itself with the layer setting.
|
||||
|
||||
## 3D
|
||||
3d models are like 3d sprites. Add them to the world, and then the engine handles drawing them. If you want special effects, its "draw" command can be overridden.
|
||||
|
||||
As sprites and 3d models are sent to render, they are added to a list; sorted; and then finally rendered.
|
||||
|
||||
## THE RENDERER
|
||||
## Fully scriptable
|
||||
|
||||
The render layer is where you do larger scale organizing. For example, for a single outline, you might have an object's draw method be the standard:
|
||||
- draw the model, setting stencil
|
||||
- draw a scaled up model with a single color
|
||||
|
||||
But, since each object doing this won't merge their outlines, you need a larger order solution, wherein you draw *all* models that will be outlined, and then draw *all* scaled up models with a single color. The render graph is how you could do that. The render graph calls draw and render functions; so with a tag system, you can essentially choose to draw whatever you want. You can add new shadow passes; whatever. Of course, prosperon is packed with some standard render graphs to utilize right away.
|
||||
|
||||
Each graphical drawing command has a specific pipeline. A pipeline is a static object that defines every rendering detail of a drawing command.
|
||||
|
||||
A drawing command is composed of:
|
||||
- a model
|
||||
- a material
|
||||
- a pipeline
|
||||
|
||||
The engine handles sorting these and rendering them effectively. There exist helper functions, like "render.image" which will in turn create a material and use the correct model.
|
||||
|
||||
You execute a list of drawing commands onto a render target. This might be the computer screen; it might be an offscreen target.
|
||||
|
||||
The material's properties are copied into the shader on a given pipeline; they also can have extra properties like "castshadows", "getshadows", and so on.
|
||||
|
||||
An *image* is a struct {
|
||||
texture: GPU texture
|
||||
rect: UV coordinates
|
||||
}
|
||||
|
||||
## 2D drawing commands
|
||||
The 2d drawing commands ultimately interface with a VERY limited subset of backend knowledge, and so are easily adaptable for a wide variety of hardware and screen APIs.
|
||||
|
||||
The basic 2D drawing techniques are:
|
||||
Sprite - arbitrarily blit a bitmap to the screen with a given affine transformation and color
|
||||
Tiles - Uniform squares in a grid pattern, drawn all on a single layer
|
||||
Text - Generates whatever is needed to display text wrapped in a particular way at a particular coordinate
|
||||
Particles - a higher order construction
|
||||
Geometry - programmer called for circles or any other arbitrary shape. Might be slow!
|
||||
|
||||
## Effects
|
||||
An "effect" is essentially a sequence of render commands. Typically, a sprite draws itself to a screen. It may have a unique pipeline for a special effect. But it might also have an "effect", which is actually a sequence of draw instructions. An example might be an outline scenario, where the sprite draws a black version of it scaled 1.1x, and then draws with the typical pipeline afterwards.
|
||||
|
||||
## A frame
|
||||
During a frame, the engine finds everything that needs rendered. This includes enabled models, enabled sprites, tilemaps, etc. This also includes programmer directions inside of the draw() and hud() functions.
|
||||
|
||||
This high level commands are culled down, accounting for off screen sprites, etc, into a more compact command queue. This command queue is then rendered in whichever way the backend sees fit. Each "command queue" maps roughly into a "render pass" in vulkan. Once you submit a command queue, the data is sorted, required data is uploaded, and a render pass draws it to the specified frame.
|
||||
|
||||
A command is kicked off with a "batch" command.
|
||||
|
||||
var batch = render.batch(target, clearcolor) // target is the target buffer to draw onto
|
||||
target must be known when the batch starts because it must ensure the pipelines fed into it are compatible. If clearcolor is undefined, it does not erase what is present on the target before drawing. To disable depth, simply do not include a depth attachment in the target.
|
||||
|
||||
batch.draw(mesh, material, pipeline)
|
||||
This is the most fundamental draw command you can do. In modern parlance, the pipeline sets up the GPU completely for rendering (stencil, blend, shaders, etc); the material plugs data into the pipeline, via reflection; the mesh determines the geometry that is drawn. A mesh defines everything that's needed to kick of a draw call, including if the buffers are indexed or not, the number of indices to draw, and the first index to draw from.
|
||||
|
||||
batch.viewport()
|
||||
|
||||
batch.sprite
|
||||
|
||||
|
||||
batch.text // a text object. faster than doing each letter as a sprite, but less flexible
|
||||
// etc
|
||||
batch.render(camera)
|
||||
|
||||
Batches can be saved to be executed again and again. So, one set of batches can be created, and then drawn from many cameras' perspectives. batch.render must take a camera
|
||||
|
||||
Behind the scenes, a batch tries to merge geometry, and does reordering for minimum pipeline changes behind the scenes.
|
||||
|
||||
Each render command can use its own unique pipeline, which entails its own shader, stencil buffer setup, everything. It is extremely flexible. Sprites can have their own pipeline.
|
||||
|
||||
ULTIMATELY:::
|
||||
This is a much more functional style than what is typically presented from graphics APIs. Behind the scenes these are all translated to OpenGL or whatever; being functional at this level helps to optimize.
|
||||
|
||||
IMPORTANT NOTE:
|
||||
Optimization only happens at the object level. If you have two pipelines with the exact same characteristics, they will not be batched. Use the exact same pipeline object to batch.
|
||||
|
||||
## SCENARIOS
|
||||
|
||||
BLOOM BULLETS
|
||||
You want to draw a background; some ships; and some bullets that have glow to them. This amounts to two ideas:
|
||||
1) draw the background and ships
|
||||
2) draw bullets to a texture
|
||||
3) apply bloom on the bullet
|
||||
4) draw bullets+bloom over the background and ships
|
||||
|
||||
Steps 1, and 2-3, can be done in parallel. They constitute their own command queues. When both are done, the composite can then happen.
|
||||
|
||||
var bg_batch = render.batch(surf1, camera);
|
||||
bg_batch.draw(background)
|
||||
bg_batch.draw(ships)
|
||||
bg_batch.end()
|
||||
|
||||
var bullet_batch = render.batch(surf2, camera);
|
||||
bullet_batch.draw(bullets)
|
||||
bullet_batch.end()
|
||||
|
||||
var bloom = render.batch(surf3, postcam)
|
||||
bloom.draw(bullet_batch.color, bloom_pipeline)
|
||||
bloom.end()
|
||||
|
||||
var final = render.batch(swapchain)
|
||||
final.draw(bg_batch.color)
|
||||
final.draw(bloom.color)
|
||||
final.end()
|
||||
|
||||
When 'batch.end' is called, it reorders as needed, uploads data, and then does a render pass.
|
||||
|
||||
3D GAME WITH DIRECTIONAL LIGHT SHADOW MAP
|
||||
|
||||
var shadow_batch = render.batch(shadow_surf, dir_T)
|
||||
shadow_batch.draw(scene, depth_mat) // scene returns a list of non culled 3d obejcts; we force it to use depth_mat
|
||||
shadow_batch.end()
|
||||
|
||||
base_mat.shadowmap = shadow_batch.color;
|
||||
|
||||
var main_batch = render.batch(swapchain, camera)
|
||||
main_batch.draw(scene)
|
||||
main_batch.end()
|
||||
|
||||
FIERY LETTERS
|
||||
This pseudo code draws a "hello world" cutout, with fire behind it, and then draws the game's sprites over that
|
||||
|
||||
var main = render.batch(swapchain, 2dcam)
|
||||
main.draw("hello world", undefined, stencil_pipeline)
|
||||
main.draw(fire)
|
||||
main.draw(fullscreen, undefined, stencil_reset)
|
||||
main.draw(game)
|
||||
main.end()
|
||||
@@ -980,6 +980,10 @@ delete globalThis.RegExp
|
||||
|
||||
// TODO: delete globalThis
|
||||
|
||||
var mem = js.calc_mem()
|
||||
log.console(json.encode(mem))
|
||||
log.console("total memory usage: " + text(mem.memory_used_size/1024) + " KB");
|
||||
|
||||
$_.clock(_ => {
|
||||
var val = locator.symbol.call(null, $_, cell.args.arg);
|
||||
|
||||
|
||||
7
mkdocs.yml
Normal file
7
mkdocs.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
site_name: Cell
|
||||
repo_url: https://gitea.pockle.world/john/cell
|
||||
docs_dir: docs
|
||||
|
||||
theme:
|
||||
name: mkdocs
|
||||
color_mode: auto
|
||||
Reference in New Issue
Block a user